JVM內存堆佈局圖解分析

Redis技術交流羣 481804090算法

JAVA可以實現跨平臺的一個根本緣由,是定義了class文件的格式標準,凡是實現該標準的JVM都可以加載並解釋該class文件,據此也能夠知道,爲啥Java語言的執行速度比C/C++語言執行的速度要慢了,固然緣由確定不止這一個,如在JVM中沒有數據寄存器,指令集使用的是棧來保存中間數據...等,儘管Java的貢獻者們爲執行速度的提升想了各類辦法,如JIT、動態編譯器等,如下是Leetcode中一道題目用不一樣的語言實現時的執行性能對比圖...架構

如下是JVM的一個基本架構圖,在這個基本架構圖中,棧有兩部份,Java線程棧以及本地方法棧,棧的概念與C/C++程序基本上都是一個概念,裏面存放的都是棧幀,一個棧幀表明的就是一個函數的調用,在棧幀裏面存放了函數的形參,函數的局部變量, 返回地址等,可是與C/C++的一個重要區別是,C/C++裏面有傳值以及傳址的區別,當傳的是一個對象時( 結構體也能夠當成對象,其實就是對象~,只不過裏面的方法默認都是public的,不信你能夠試試,在結構體中加一個函數,編譯器也不會報錯,程序依舊運行~~~),會將對象復到到棧中,而Java中只有基本類型纔是傳值的,其餘類型傳的都是引用,什麼是引用,學過C/C++的就把引用看成指針理解吧~~~,在這個基本架構圖中,能夠看出JVM還定義了一個本地方法棧,本地方法棧是爲Java調用本地方法【這些本地方法是由其餘語言編寫的】服務的xss

上面的圖中看到的是JVM中棧有兩個,可是堆只有一個,每個線程都有自已的線程棧【線程棧的大小能夠經過設置JVM-xss參數進行配置,32位系統下,通常默認的大小是512K】,線程棧裏面的數據屬於該線程私有,可是全部的線程都共享一個堆空間,堆中存放的是對象數據,什麼是對象數據,排除法,排除基本類型以及引用類型之外的數據都將放在堆空間中,下面來具體分析一下堆空間...函數

在JVM中堆空間劃分以下圖所示性能

 

上圖中,刻畫了Java程序運行時的堆空間,能夠簡述成以下2條優化

1.JVM中堆空間能夠分紅三個大區,新生代、老年代、永久代spa

2.新生代能夠劃分爲三個區,Eden區,兩個倖存區線程

在JVM運行時,能夠經過配置如下參數改變整個JVM堆的配置比例3d

1.JVM運行時堆的大小

  -Xms堆的最小值

  -Xmx堆空間的最大值

2.新生代堆空間大小調整

  -XX:NewSize新生代的最小值

  -XX:MaxNewSize新生代的最大值

  -XX:NewRatio設置新生代與老年代在堆空間的大小

  -XX:SurvivorRatio新生代中Eden所佔區域的大小

3.永久代大小調整

  -XX:MaxPermSize

4.其餘

   -XX:MaxTenuringThreshold,設置將新生代對象轉到老年代時須要通過多少次垃圾回收,可是仍然沒有被回收

在上面的配置中,老年代所佔空間的大小是由-XX:SurvivorRatio這個參數進行配置的,看完了上面的JVM堆空間分配圖,可能會奇怪,爲啥新生代空間要劃分爲三個區Eden及兩個Survivor區?有何用意?爲何要這麼分?要理解這個問題,就得理解一下JVM的垃圾收集機制(複製算法也叫copy算法),步驟以下:指針

複製(Copying)算法
將內存平均分紅A、B兩塊,算法過程:
1. 新生對象被分配到A塊中未使用的內存當中。當A塊的內存用完了, 把A塊的存活對象對象複製到B塊。
2. 清理A塊全部對象。
3. 新生對象被分配的B塊中未使用的內存當中。當B塊的內存用完了, 把B塊的存活對象對象複製到A塊。
4. 清理B塊全部對象。
5. goto 1。
優勢:簡單高效。缺點:內存代價高,有效內存爲佔用內存的一半。
圖解說明以下所示:(圖中後觀是一個循環過程)
 
對複製算法進一步優化:使用Eden/S0/S1三個分區
平均分紅A/B塊太浪費內存,採用Eden/S0/S1三個區更合理,空間比例爲Eden:S0:S1==8:1:1,有效內存(便可分配新生對象的內存)是總內存的9/10。
算法過程:
1. Eden+S0可分配新生對象;
2. 對Eden+S0進行垃圾收集,存活對象複製到S1。清理Eden+S0。一次新生代GC結束。
3. Eden+S1可分配新生對象;
4. 對Eden+S1進行垃圾收集,存活對象複製到S0。清理Eden+S1。二次新生代GC結束。
5. goto 1。
 
默認Eden:S0:S1=8:1:1,所以,新生代中可使用的內存空間大小佔用新生代的9/10,那麼有人就會問,爲何不直接分紅兩個區,一個區佔9/10,另外一個區佔1/10,這樣作的緣由大概有如下幾種
1.S0與S1的區間明顯較小,有效新生代空間爲Eden+S0/S1,所以有效空間就大,增長了內存使用率
2.有利於對象代的計算,當一個對象在S0/S1中達到設置的XX:MaxTenuringThreshold值後,會將其分到老年代中,設想一下,若是沒有S0/S1,直接分紅兩個區,該如何計算對象通過了多少次GC還沒被釋放,你可能會說,在對象里加一個計數器記錄通過的GC次數,或者存在一張映射表記錄對象和GC次數的關係,是的,能夠,可是這樣的話,會掃描整個新生代中的對象, 有了S0/S1咱們就能夠用S0/S1記錄對象通過的代數【固然這取決於具體的 Java虛擬機的實現了】~~~
 
GC實例分析,下次再分析~
相關文章
相關標籤/搜索