JVM Garbage First 垃圾回收器

JVM Garbage First 垃圾回收器

定義:Garbage First

  • 2004論文發佈
  • 2009 JDK 6u14 體驗
  • 2012 JDK 7u4 官方支持
  • 2017 JDK 9 默認

使用場景:

  • 同時注重吞吐量(Throughput)和低延遲(Low latency),默認的暫停目標是200 ms
  • 超大堆內存,會將堆劃分爲多個大小相等的Region
  • 整體上是標記+整理算法,兩個區域之間是複製算法

相關JVM參數

-XX:+UseG1GC

-XX:G1HeapRegionSize=size:堆內存Region大小

-XX:MaxGCPauseMillis=time:暫停目標時間

在這裏插入圖片描述

1、Young Collection

會發生 STW(Stop The World)

  • 初始階段,新生成的對象會進入伊甸園

在這裏插入圖片描述

  • 在發生垃圾回收時,伊旬園中的未被回收的對象會進入倖存區

在這裏插入圖片描述

  • 再次發生垃圾回收時,伊旬園中的未被回收的新對象會複製到一個新的倖存區(Region塊),並將舊倖存區中達到指定閾值的對象複製到老年代,沒有被回收且沒有達到閾值的對象複製到新的倖存區

在這裏插入圖片描述

2、Young Collection + CM

  • 在 Young GC 會進行 GC Root 的初始標記
  • 老年代佔用堆空間比例達到閾值時,進行併發標記(不會 STW),由下面的 JVM 參數決定
-XX:InitiatingHeapOccupancyPercent=percent (默認爲45%)

在這裏插入圖片描述

3、Mixed Collection

會對伊甸園、倖存區、老年代進行全面的垃圾回收

  • 最終標記(Remark)會發生 STW
  • 拷貝存貨(Evacuation)會發生 STW
-XX:MaxGCPauseMillis=ms

在此階段伊甸園中的未被回收的對象會複製到倖存區中,同時倖存區中也會進行垃圾回收,將倖存區中未被回收的對象複製到新的倖存區中,壽命到達指定閾值的對象複製到老年代中。在回收老年代時也是同樣使用複製算法,並根據設置的最大暫停時間,優先回收其中回收價值最高的部分老年代中的對象。

在這裏插入圖片描述

4、Full GC

  • Serial GC
    • 新生代內存不足發生的垃圾回收:Minor GC
    • 老年代內存不足發生的垃圾回收:Full GC
  • Parallel GC
    • 新生代內存不足發生的垃圾回收:Minor GC
    • 老年代內存不足發生的垃圾回收:Full GC
  • CMS
    • 新生代內存不足發生的垃圾回收:Minor GC
    • 到老年代所佔空間達到設置的閾值時,會觸發併發標記後混合回收,當併發垃圾回收失敗時就會發生Full GC
  • G1
    • 新生代內存不足發生的垃圾回收:Minor GC
    • 到老年代所佔空間達到設置的閾值時,會觸發併發標記後混合回收,此時,垃圾回收的速度小於垃圾產生的速度時,就會發生Full GC

5、Young Collection 跨帶引用

  • 新生代回收的跨代引用(老年代引用新生代)

  • 在新生代中進行垃圾回收時,要找到根對象(GC Root)進行可達性分析,而部分新生代中對象的根對象(GC Root)時在老年代中的。而遍歷老年代查找根對象效率較低,因此使用卡表(Card Table)將老年代細分爲卡(Card)組成的卡表(Card Table),當老年代中的對象引用了新生代中的對象,則會把老年代中對應的卡(Card)標記爲髒卡(Dirty Card)。此時,在老年代中的對象引用新生代中的對象後,進行垃圾回收時就不必遍歷整個老年代,提升垃圾回收的效率。

  • 在新生代對象中有一個Remembered Set,記錄着老年代中引用此新生代對象的髒卡(Dirty Card),在進行垃圾回收時就可以通過Remembered Set找到對應的髒卡(Dirty Card),然後通過髒卡(Dirty Card)遍歷根對象(GC Root)。

  • 在對象引用變更時通過寫後屏障(Post-Write Barrier)和髒卡隊列(Dirty Card Queue)

  • Remembered Set通過Concurrent Refinement Threads更新

在這裏插入圖片描述

6、Remark

在垃圾回收器進行併發標記階段時,會在可以回收的對象上加入寫屏障(Pre-Write Barrier),以防對象在標記後發生引用變化後依舊清除對象。

在這裏插入圖片描述

在這裏插入圖片描述

當添加寫屏障(Pre-Write Barrier)的對象發生引用變化之後,寫屏障會將對象加入satb_mark_queue,在重新標記(Remark)時(會發生 STW)就會重新判斷satb_mark_queue中的對象是否可以被清除。

在這裏插入圖片描述

7、JDK 8u20 字符串去重

  • 優點:節省大量內存
  • 缺點:略微多佔用了cpu時間,新生代回收時間略微增加
-XX:UseStringDeduplication
String s1 = new String("hello"); //char[]{'h', 'e', 'l', 'l', 'o'}
String s2 = new String("hello"); //char[]{'h', 'e', 'l', 'l', 'o'}
  • 將所有新分配的字符串放入一個隊列
  • 當新生代回收時,G1併發檢查是否有字符串重複
  • 如果它們值一樣,讓它們引用同一個char[]

注意:此字符串去重與String.intern()不同,String.intern()關注的是字符串對象而字符串去重關注的是char[]在JVM內部,使用了不同的字符串表。

8、JDK 8u40 併發標記類卸載

所有對象都經過併發標記後,就知道哪些類不再被使用,當一個類加載器的所有類都不在使用,則卸載它所加載的所有類

-XX:+ClassUnloadingWithConcurrentMark // 默認啓用

9、JDK 8u60 回收巨型對象

  • 一個對象大於Regin的一半時,稱之爲巨型對象
  • G1不會對巨型對象進行拷貝
  • 回收時被優先考慮
  • G1會跟蹤老年代所有incoming引用,這樣老年代incoming引用爲0的巨型對象就可以在新生代垃圾回收時處理掉

在這裏插入圖片描述

10、JDK 9 併發標記起始時間調整

  • 併發標記必須在堆空間佔滿前完成,否則退化爲Full GC
  • JDK 9 之前需要使用-XX:InitiatingHeapOccupancyPercent
  • JDK 9 可以動態調整
    • -XX:InitiatingHeapOccupancyPercent 用來設置初始值
    • 進行數據採樣並動態調整
    • 總會添加一個安全的空檔空間
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章