觸發Full GC的時機


由於Full GC的耗時是Minor GC的十倍左右,所以Full GC的頻率設計得比Minor GC低得多。現總結一下觸發Full GC的情況。

在那些實現了CMS的比較新的虛擬機中,如果配置了-XX:+UseConcMarkSwapGC,則啟用CMS回收算法,CMS會周期性地檢查老年代的情況,每隔一定時間(默認2秒),就檢查是否需要對老年代進行一次CMS回收,判斷的依據如下:

1、如果沒有設置-XX:+UseCMSInitiatingOccupancyOnly,虛擬機會根據收集的數據決定是否觸發(建議線上環境帶上這個參數,不然會加大問題排查的難度)。
2、老年代使用率達到閾值 CMSInitiatingOccupancyFraction,默認92%。
3、永久代的使用率達到閾值 CMSInitiatingPermOccupancyFraction,默認92%,前提是開啟 CMSClassUnloadingEnabled
4、新生代的晉升擔保失敗。
 

 

CMS分為兩種模式,background和foreground,background采用concurrent remark模式,可以和用戶進程並行,而foreground則必須要stop the world(STW)。周期性的CMS采用的是background的方式,而主動的GC則采用foreground方式。主動的GC肯定是Full GC,反之則未必,因為Full GC不是只在CMS中存在的,並且Full GC也可以是並行的Full GC(區別於正常的Full GC),采用並行的Full GC在Old GC階段走的是background式的CMS,可參考寒泉子《jvm源碼分析之SystemGC完全解讀》一文。本文的重點也是Full GC。

因為Full GC所用的時間較長,為了充分發揮CMS的優勢,通常都會配置-XX:+UseCMSInitiatingOccupancyOnly,讓CMS周期性的以和用戶現場並行的方式進行垃圾回收,這也是CMS設計的初衷。

但是呢,因為CMS采用標記-清理的方式進行GC,所以會產生碎片,時間久了,碎片就會很多,所以一般來說,進行了一段時間CMS之后,如果開啟了UseCMSCompactAtFullCollection(默認為true開啟),在foreground的時候就要采用一次壓縮,這時采用Serial Old或Parallel Old這些采用標記-整理算法的GC方式(CMS采用的是標記-清理算法)進行回收。具體多少次full gc (foreground CMS)之后進行一次壓縮,取決於-XX:CMSFullGCsBeforecompact(默認為0)。源碼如下:

*should_compact = UseCMSCompactAtFullCollection && 
((_full_gcs_since_conc_gc >= CMSFullGCsBeforeCompaction)
|| GCCause::is_user_requested_gc(gch->gc_cause())
|| gch->incremental_collection_will_fail(true /* consult_young */)
);

 從源碼可以看出,除了達到一定次數之外,如果用戶調用了System.gc()以及發生了promotion failed,也會進行一次壓縮。同時也可以看出,foreground不一定會采用壓縮,所以那些說foreground就是mark swap compact(msc)的是不對的。

但是周期性的CMS(background)只會回收老年代,除了周期性的進行GC之外,還有一些緊急情況,需要主動觸發GC(foreground),主動觸發的GC會連帶一次Minor GC,所以也稱為Full GC。主動觸發的GC是會暫停所有用戶現場的,俗稱stop the world(STW)。

在那些沒有實現CMS的老虛擬機或者沒有開啟CMS的虛擬機中,每一次Old GC都是Full GC,且會STW,當然因為除了CMS之外,其他的老年代回收算法都是采用標記-整理的方式,所以肯定也是壓縮的,。

如果正在進行CMS回收,又觸發了一次Full GC,則Full GC會搶占回收執行機會,停止CMS,采用Serial Old或Parallel Old這些采用標記-整理算法的GC方式(CMS采用的是標記-清理算法)進行Full GC。

下面是一些會觸發Full GC的情況

1.System.gc()

在沒有開啟DisableExplicitGC的情況下,雖然只是建議,但是很多情況下,都會調用Full GC的,比如在原本應進行CMS的時候。System.gc()一般都是用在要釋放堆外內存的時候使用。

2.老生代內存不足的時候

這種情況通常是對象要晉升到老年代中時,發現老年代的內存不足了,所以要引發一次Full GC。對象的晉升分為正常晉升和提前晉升。

3.永生區空間不足時

有些虛擬機把方法區也放到堆中管理,當加載的類太多時,永生區內存不足需要回收,也會觸發Full GC

4.冒險失敗之后

 在Minor GC時發現to去的內存不足,則將Eden區和from區的內存全部晉升到老年區,清空新生代。但是如果此時老年區內存不足,則會冒險失敗,冒險失敗之后,對象仍然留在新生代(此時的Eden區和from區都接近99%),然后出發一次Full GC,這樣便於下次如果還有冒險,可以增加冒險成功的幾率。

5.HandlePromotionFailure設置為false或者歷年平均晉升對象的大小大余老年代剩余連續空間

在Minor GC之前,虛擬機會檢查老年代剩余連續空間是否大余新生代所有對象總大小,如果大余,則說明Minor GC絕對安全;如果小於,則會檢查HandlePromotionFailure設置是否擔保失敗,如果不擔保,則在Minor GC之前進行一次Full GC;如果擔保,則再檢查歷年平均晉升對象的大小是否大余老年代剩余連續空間,如果大余,則不冒險,在Minor GC之前進行一次Full GC;如果小於,則冒險,進入情況4。

6.分配大對象時(和2其實是一樣的,但是因為特殊,所以單獨拿出來說)

如果直接要分配一個大對象,並且這個大對象的大小超過Eden區的一半,這個對象就會直接分配在老年代,此時如果老年代空間不足,出發一次Full GC,而不出發Minor GC。但是需要注意的是,如果分配的是TLAB而不是真正的大對象,那么不會導致full gc,而是調整TLAB的大小。

7.執行jmap -histo:live或者jmap -dump:live的時候

這屬於強制讓虛擬機執行一次full GC。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM