天天看點

Java 記憶體區域與記憶體溢出異常

一.運作時資料區域

1 . 程式計數器(Program Counter Register):

是一塊較小記憶體空間,它可看作是目前線程所執行的位元組碼的行号訓示器。各線程都會享有自己獨立的程式計數器,這樣保證線程之前計資料器互不影響,是以這類記憶體區域為“線程私有”的記憶體。

2 . java虛拟機棧(Java Virtual Machine Stacks):

也是線程私有的,它的生命同期與線程相同,它描述的是Java方法執行的記憶體模型:每個方法在運作的時候會建立一個棧桢用于存儲局部變量表、操作數棧、動态連結、方法出口等。在該記憶體區域有兩種異常情況:StackOverflowError,當線程請求的棧深度超出虛拟機所允許的深度地抛出該異常;OutOfMemoryError,若虛拟機棧可動态擴充的,而當擴充時無法申請到足夠的記憶體就會抛出該異常。

3 .本地方法棧

與虛拟機棧很相似,差別是虛拟機棧為虛拟機執行Java方法服務,本地方法棧則為虛拟機執行Native方法服務

4 .Java 堆

Java堆是虛拟機管理的記憶體中最大的一塊,是被所有線程共享的一塊記憶體區域,在虛拟機啟動地建立,此記憶體區域唯一目的就是存放對象執行個體。Java堆是垃圾收集順管理的主要區域,是以也被稱做”GC堆“。由于現在收集器基本都采用分代碼收集算法,是以Java堆又可細分為:新生代和老年代,再細緻一點又有:Eden空間、FormSurvivor空間、To Survivor空間。如此劃分的目的是為了更好的回收記憶體,或者更快的配置設定記憶體。

5 .方法區

方法區與Java堆一樣是各個線程共享的記憶體區域,它用于存儲已被虛拟機加載的類資訊、常量、靜态變量、即時編譯器編譯後的代碼等資料。它有一個别名叫Non-Heap(非堆),目的大概是與Java堆區分開來。在HotSpot虛拟機上又被稱為”永久代“。而我經常使用的虛拟機就是HotSpot,永久大小可通過下面參數進行配制:

-XX:PermSize來指定初始化是永久代大小和

-XX:MaxPermSize來指定永久代最大記憶體。

當方法區無法滿足記憶體靈機一動需求時,将抛出OutOfMemoryError異常。

6 .運作時常量池

運作時常量池是方法區的一部分。Class檔案中除了有類的版本、字段、方法、接口等描述資訊外,還有一項資訊是常量池,用于存放 編譯期生成的各種字面量和符号 引用,這部分内容将在類加載後進入方法區的運作時常量池中存放。一般來說,除了儲存Class檔案中描述的符号引用外,還會把翻譯出來的直接引用也存儲在運作時常量池中。

7 .直接記憶體

在JDK1.4中新加入NIO(New Input/Output)類,引入了一種基于通道與緩沖區(Buffer)的I/O方式,它可以使用Native函數庫直接配置設定堆外記憶體,然後通過 一個存儲在Java堆中的DirectByteBuffer對象作為這塊記憶體的引用進行操作。這樣能在一些場景中提高性能,因為避免了在Java堆和Native堆中來回複制資料。

二對象建立

1、遇到new指令時,首先在常量池中尋找該在的符号引用,并且檢查這個符号引用代表 類是否加載、解析和初始經過,如果沒有,那執行相應加載過程。

2、虛拟機為新生對象配置設定記憶體。所需記憶體大小在類加載完成後即可完全确定。虛拟機需從堆中劃分相同大小的記憶體空間。劃分方式:指針碰撞和空閑清單,使用哪種方式是由所采用的器是否帶有壓縮整理功能決定。由于對象建立非常頻繁的,在并發情況下并不是線程安全的。解決這個問題有兩種方案:一種,對配置設定記憶體空間的動作進行同步處理;另一種按線程劃分在不同空間中進行,即每個線程在Java堆中預先配置設定一小塊記憶體,稱為本地線程配置設定緩沖TLAB。虛拟機是否使用TLAB,可通過:

-XX:+/-UseTLAB參數來設定。

3、将配置設定到的記憶體空間都初始化為零值,這步操作就是Java對象屬性預設值的來源;

4、設定對象頭,如對象是哪個類的執行個體、如何才能找到類的中繼資料資訊、對象的哈希碼、GC分代年齡等資訊。

三、記憶體洩漏與記憶體溢出

1 .記憶體洩漏

是指,為對象配置設定了記憶體空間後,無法釋放已申請的記憶體空間,一次記憶體洩露危害可以忽略,但記憶體洩露堆積後果很嚴重,無論多少記憶體,遲早會被占光。memory leak會最終會導緻out of memory!

2 .記憶體溢出

是指,為對象申請内容空間時,無法申請到足夠空間而抛出OutOfMemoryError。

3 .隐式洩漏

是指,一個線程或方法長時間運作在虛拟機中,導緻與該線程相關的記憶體區域不能及時釋放,但當線程或方法一旦運作完成,與線程相關的内在區域即可釋放。

總結:從使用者使用程式的角度來看,記憶體洩漏本身不會産生什麼危害,作為一般的使用者,根本感覺不到記憶體洩漏的存在。真正有危害的是記憶體洩漏的堆積,這會最終消耗盡系統所有的記憶體。從這個角度來說,一次性記憶體洩漏并沒有什麼危害,因為它不會堆積,而隐式記憶體洩漏危害性則非常大,因為較之于常發性和偶發性記憶體洩漏它更難被檢測到

四、JVM常用參數

-Xms40m  #指虛拟機啟動時即建立40M的堆空間
-Xmx512m #指堆了大值為512M
-XX:MaxPermsize=m  #指永久代最大值為256M。 
-Xverify:none #禁止位元組碼驗證過程
-XX:+DisableExplicitGC  #屏蔽System.gc()操作。
-Xoss40m #設定本地方法棧大小,這裡亂寫的。
-Xss1024k  #設定棧容量
-XX:HeapDumpOnOutOfMemoryError  #讓虛拟機在出現記憶體逆風異常地Dump出目前的記憶體堆轉存儲快照。
-XX:PretenureSizeThreshold=m #申請對象大于2M時直接進入老年代
-XX:+DisableExplicitGC ##禁用系統中的System.gc()
-XX:+BackgroundCompilation #啟用JIT編譯優化
-Xloggc:/home/xieyu/logs/gc.log  #将常量GC日志輸出到檔案
-XX:CMSInitiatingOccupancyFraction=  ##當老年代使用率達70%之後啟動CMS GC
           

垃圾收集器選擇