JVM 系列文章之 Full GC 和 Minor GC

Full GC

Full GC 就是收集整個堆,包括新生代,老年代,永久代(在JDK 1.8及之後,永久代會被移除,換爲metaspace)等收集全部部分的模式html

RednaxelaFX大Major GC和Full GC的區別是什麼?觸發條件呢?- 知乎這個問題有關於 GC分類的回答:算法

針對 HotSpot VM的實現,它裏面的GC其實準確分類有兩種:segmentfault

  • Partial GC(局部 GC): 並不收集整個 GC 堆的模式
    • Young GC: 只收集young gen的GC,Young GC還有種說法就叫作 "Minor GC"
    • Old GC: 只收集old gen的GC。只有垃圾收集器CMS的concurrent collection 是這個模式
    • Mixed GC: 收集整個young gen 以及部分old gen的GC。只有垃圾收集器 G1有這個模式
  • Full GC: 收集整個堆,包括 新生代,老年代,永久代(在 JDK 1.8及之後,永久代被移除,換爲metaspace 元空間)等全部部分的模式

Full GC的觸發條件

針對不一樣的垃圾收集器,Full GC的觸發條件可能不都同樣。按HotSpot VM的serial GC的實現來看,觸發條件是:數組

  • 當準備要觸發一次 young GC時,若是發現統計數聽說以前 young GC的平均晉升大小比目前的 old gen剩餘的空間大,則不會觸發young GC而是轉爲觸發 full GC (由於HotSpot VM的GC裏,除了垃圾回收器 CMS的concurrent collection 以外,其餘能收集old gen的GC都會同時收集整個GC堆,包括young gen,因此不須要事先準備一次單獨的young GC)網絡

  • 若是有永久代(perm gen),要在永久代分配空間但已經沒有足夠空間時,也要觸發一次 full GC併發

  • System.gc(),heap dump帶GC,其默認都是觸發 full GC.jvm

HotSpot VM裏其餘非併發GC的觸發條件複雜一些,不過大體原理與上面說的其實同樣。性能

在 Parallel Scavenge 收集器下,默認是在要觸發 full GC前先執行一次 young GC,而且兩次GC之間能讓應用程序稍微運行一小下,以期下降 full GC的暫停時間 (由於 young GC 會盡可能清理了young gen的死對象,減小了 full GC的工做量)。控制這個行爲的VM參數是: -XX:+ScavengeBeforeFullGC優化

併發GC的觸發條件就不同,以 CMS GC爲例,它主要是定時去檢查old gen的使用量,但使用量超過了觸發比例就會啓動一次 CMS GC,對old gen作併發收集spa

Minor GC

Minor GC 是俗稱,新生代(新生代分爲一個 Eden區和兩個Survivor區)的垃圾收集叫作 Minor GC。在 Oracle 高級研究員鄭雨迪的極客時間專欄 《深刻拆解Java虛擬機》中也談到 Minor GC,內容以下:

觸發條件

當 Eden 區的空間耗盡了怎麼辦?這個時候 Java虛擬機便會觸發一次 Minor GC來收集新生代的垃圾,存活下來的對象,則會被送到 Survivor區。

簡單說就是當新生代的Eden區滿的時候觸發 Minor GC

Minor GC的過程

前面提到,新生代共有 兩個 Survivor區,咱們分別用 from 和 to來指代。其中 to 指向的Survivor區是空的。

當發生 Minor GC時,Eden 區和 from 指向的 Survivor 區中的存活對象會被複制(此處採用標記 - 複製算法)到 to 指向的 Survivor區中,而後交換 from 和 to指針,以保證下一次 Minor GC時,to 指向的 Survivor區仍是空的

注意: from與to只是兩個指針,它們變更的,to指針指向的Survivor區是空的

Survivor區對象晉升位老年代對象的條件

Java虛擬機會記錄 Survivor區中的對象一共被來回複製了幾回。若是一個對象被複制的次數爲 15 (對應虛擬機參數 -XX:+MaxTenuringThreshold),那麼該對象將被晉升爲至老年代,(至於爲何是 15次,緣由是 HotSpot會在對象頭的中的標記字段裏記錄年齡,分配到的空間只有4位,因此最多隻能記錄到15)。另外,若是單個 Survivor 區已經被佔用了 50% (對應虛擬機參數: -XX:TargetSurvivorRatio),那麼較高複製次數的對象也會被晉升至老年代。

當Survivor區的部分對象晉升到老年代後,老年代的佔用量一般會升高。

注意

在Minor GC過程當中,Survivor 可能不足以容納Eden和另外一個Survivor中的存活對象。若是Survivor中的存活對象溢出,多餘的對象將被移到老年代,這稱爲過早提高(Premature Promotion),這會致使老年代中短時間存活對象的增加,可能會引起嚴重的性能問題。再進一步說,在Minor GC過程當中,若是老年代滿了而沒法容納更多的對象,Minor GC 以後一般就會進行Full GC,這將致使遍歷整個Java堆,這稱爲提高失敗(Promotion Failure)。至於解決辦法,這就涉及到對應用程序的調優問題了,這裏就不敘述了,若有興趣,請自行查閱相關資料

Minor GC的問題與卡表分析

Minor GC存在一個問題就是,老年代的對象可能引用新生代的對象,在標記存活對象的時候,就須要掃描老年代的對象,若是該對象擁有對新生代對象的引用,那麼這個引用也會被做爲 GC Roots。這至關於就作了全堆掃描

JVM如何避免Minor GC掃描全堆

HotSpot 給出的解決方案是 一項叫作 卡表 的技術。以下圖所示:

card table

卡表的具體策略是將老年代的空間分紅大小爲 512B的若干張卡,而且維護一個卡表,卡表本省是字節數組,數組中的每一個元素對應着一張卡,其實就是一個標識位,這個標識位表明對應的卡是否可能存有指向新生代對象的引用,若是可能存在,那麼咱們認爲這張卡是髒的,即髒卡。如上圖所示,卡表3被標記爲髒。

在進行Minor GC的時候,咱們即可以不用掃描整個老年代,而是在卡表中尋找髒卡,並將髒卡中的老年代指向新生代的引用加入到 Minor GC的GC Roots裏,當完成全部髒卡的掃描以後,Java 虛擬機便會將全部髒卡的標識位清零。這樣虛擬機以空間換時間,避免了全表掃描

關於 Major GC的說明

除了Full GC和Minor GC外,還有一種說法叫作 "Major GC":

Major GC一般是跟full GC是等價的,收集整個GC堆,但由於 HotSpot VM發展這麼多年,外界對各類名詞的解讀已經徹底混亂了,當有人說"Major GC"的時候必定要問清楚他想要指的是上面的 full GC仍是 old GC

以上是 R大關於 Major GC的說法,比較權威的。在網上還流行着另一種說法就是 Major GC是對老年代的垃圾回收

小結

以上的內容基本上是從 R大的知乎回答與極客時間專欄 《深刻拆解Java虛擬機》總結來的,在總結過程當中,也算是對 Full GC 與 Minor GC 有了一個基本的認識。

取之網絡,再回饋之網絡,本人對於JVM是渣渣級選手,若有錯誤之處,歡迎指教

參考資料 & 鳴謝

相關文章
相關標籤/搜索