深入淺出JVM內存結構及GC機制

JVM內存結構

JAVA運行時的數據區:根據 JVM 規範,JVM在執行JAVA程序的過程中會把它所管理的內存分為虛擬機棧、本地方法棧、程序計數器(PC寄存器)、堆、方法區五個部分。

深入淺出JVM內存結構及GC機制

深入淺出JVM內存結構及GC機制

深入淺出JVM內存結構及GC機制

JVM中對象的生命週期圖如下:

深入淺出JVM內存結構及GC機制

新生代:系統啟動後,新創建的存放在Eden區中,一段時間後,大量的對象存放在Eden區中,導致Eden滿了,觸發YongGC,所採用複製算法。回收掉大量的對象。存活下來的對象被全部移動到倖存區中的From區(或者TO區),並在存活下來的對象上標記 +1。待下一次Eden區滿的時候,再次觸發YongGC,回收掉Eden區大量的對象,存活下來的對象全部移動到TO區(或者From),剛移動到TO區的對象標記+1,而從From區移動到TO的對象標記 +2。每經歷過一次YongGC 對象都會標記 +1 。下面循環往復。只有Eden區滿了, 就會進行一次YongGC, 然後把存活下來的對象移動到倖存區。 只要倖存區中的對象經歷過15次(此值大小可設置)YongGC後,待下次再經歷YongGC直接移動到Old區。

備註: 默認Eden:S0:S1=8:1:1

默認 new:old =1:2

老年代:Full GC 是發生在老年代的垃圾收集動作,所採用的是標記-清除算法。設置一個參數開啟標記整理算法,每次執行FullGC後,都會執行一次標記整理算法。老年代裡面的對象幾乎個個都是在 Survivor 區域中熬過來的,它們是不會那麼容易就 “死掉” 了的。因此,Full GC 發生的次數不會有 Minor GC 那麼頻繁,並且做一次 Full GC 要比進行一次 Minor GC 的時間更長。 另外,標記-清除算法收集垃圾的時候會產生許多的內存碎片 ( 即不連續的內存空間 ),此後需要為較大的對象分配內存空間時,若無法找到足夠的連續的內存空間,就會提前觸發一次 GC 的收集動作。

深入淺出JVM內存結構及GC機制

深入淺出JVM內存結構及GC機制

性能調優重要參數

1.JVM運行時堆的大小

-Xms堆的初始大小

-Xmx堆空間的最大值

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

-XX:NewSize新生代的最小值

-XX:MaxNewSize新生代的最大值

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

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

3、其他

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

-XX:CMSInitiatingOccupancyFraction=70:表示年老代內存空間使用到70%時就開始執行CMS收集,確保年老代有足夠的空間接納來自年輕代的對象,避免Full GC的發生。

-XX:+UseCMSCompactAtFullCollection:打開內存空間的壓縮和整理,在Full GC後執行。可能會影響性能,但可以消除內存碎片。

JVM監控命令

(一) jmap –heap pid:打印heap的概要信息,heap的配置及使用情況。

(二) jstat –gc/-gcutil pid interval count:Heapsize和垃圾回收狀況的監控

(三) java -XX:+PrintFlagsFinal -version > jvmpara.txt

討論區

(一)為什麼要把大對象不經過新生代直接放進老年代?

(二)老年代垃圾收集後碎片問題

因為年老代的併發收集器使用標記清除算法,所以不會對堆進行壓縮。當收集器回收時,他會把相鄰的空間進行合併,這樣可以分配給較大的對象。但是,當堆空間較小時,運行一段時間以後,就會出現“碎片”,如果併發收集器找不到足夠的空間,那麼併發收集器將會停止,然後使用傳統的標記、清除方式進行回收。

如果出現“碎片”,可能需要進行如下配置:

-XX:+UseCMSCompactAtFullCollection:使用併發收集器時,開啟對年老代的壓縮。

-XX:CMSFullGCsBeforeCompaction=0:上面配置開啟的情況下,這裡設置多少次Full GC後,對年老代進行壓縮。

所謂大對象,是指需要大量連續內存空間的java對象,例如很長的數組,此種對象會直接進入老年代,而老年代雖然有很大的剩餘空間,但是無法找到足夠大的連續空間來分配給當前對象,此種情況就會觸發JVM進行Full GC。

CMS和Parnew只是一種垃圾回收算法,Parnew作用在新生代,CMS則作用在老年代。

為了解決這個問題,CMS垃圾收集器提供了一個可配置的參數,

Concurrent Mark Sweep(並行標記清除),是一種以獲取最短回收停頓時間為目標的收集器,尤其重視服務的響應速度。

CMS是老年代垃圾回收器,基於標記-清除算法實現。新生代默認使用ParNew並行收集器,基於複製算法

即-XX:+UseCMSCompactAtFullCollection開關參數,用於在“享受”完Full GC服務之後額外免費贈送一個碎片整理的過程,內存整理的過程無法併發的,空間碎片問題沒有了,但提頓時間不得不變長了,JVM設計者們還提供了另外一個參數 -XX:CMSFullGCsBeforeCompaction,這個參數用於設置在執行多少次不壓縮的Full GC後,跟著來一次帶壓縮的。

(三)什麼情況下回觸發MinorGC 、FullGC?

Minor GC觸發條件:當Eden區滿時,觸發Minor GC。

Full GC觸發條件:

(1)調用System.gc時,系統建議執行Full GC,但是不必然執行

(2)老年代空間不足(老年代的使用率達到閾值(通過JVM參數:CMSInitiatingOccupancyFraction設定,默認為92%))

(3)方法去空間不足

(4)通過Minor GC後進入老年代的平均大小大於老年代的可用內存

(5)由Eden區、From Space區向To Space區複製時,對象大小大於To Space可用內存,則把該對象轉存到老年代,且老年代的可用內存小於該對象大小。

1、為了避免由於新生代對象晉升到舊生代導致舊生代空間不足的現象,在進行Minor GC時,做了一個判斷,如果之前統計所得到的Minor GC晉升到舊生代的平均大小大於舊生代的剩餘空間,那麼就直接觸發Full GC。

例如程序第一次觸發Minor GC後,有6MB的對象晉升到舊生代,那麼當下一次Minor GC發生時,首先檢查舊生代的剩餘空間是否大於6MB,如果小於6MB,則執行Full GC。

2、對於採用CMS進行老年代GC的程序而言,尤其要注意GC日誌中是否有promotion failed和concurrent mode failure兩種狀況,當這兩種狀況出現時可能會觸發Full GC。

promotion failed是在進行Minor GC時,survivor space放不下、對象只能放入老年代,而此時老年代也放不下造成的;

concurrent mode failure是在執行CMS GC的過程中同時有對象要放入老年代,而此時老年代空間不足造成的

優化方法:增大survivor space、老年代空間


分享到:


相關文章: