G1回收器
這一篇是詳細說明G1回收器的,因為他相對來說確實是個縫合怪,上篇的內容又太多了所不清楚,所有這一篇進行詳細的說明,
第一個呢就是其實G1是兼顧并行和并發的,簡單來說就是既可以并行也可以并發,最大程度利用系統資源,在實現可達的最大吞吐量的前提下,盡可能的減少STW的時間,簡單的流程就是執行GC會先STW,然后多線程執行標記或者回收實現并行,然后切換回用戶線程,實現并發。
第二個就是分代分區的不同,上一篇主包有說過G1使用了分區算法把內存分成小的區域Region,但是分代其實和其他的回收器是不同的,他沒有像其他區一樣很嚴格的進行年輕代和老年代的劃分,如圖:
?他是先分區再分代的,也就是年輕代和老年代的區域可以不連續的,那同學就會問怎么執行標記整理?老年代不得亂套啊,其實不會的還是和之前一樣向起始地址移動。
第三種就是可預測的時間模型,因為采用了分區算法和分代算法,讓年輕代和老年代的區域更加的小了,G1可以根據各個需要回收的Region來進行價值排序任何放入列表優先回收(回收的空間和回收需要的時間)。這個就比較厲害了可以在相同的時間內比其他的回收器回收的效率更高,因為可以在不同的時間里選取最優的區域進行回收。
缺點就是內存的占用是比其他任何回收器都要高的多,這也是沒辦法要維護的表和屏障就比其他的多多了,因為是縫合怪的緣故所以推薦使用的內存就是6GB+使用G1。
G1回收器的參數設置
基礎參數:
- **
-XX:+UseG1GC
**?:啟用G1垃圾回收器(必備參數)。 - ?**
-XX:MaxGCPauseMillis
?:設置目標最大停頓時間(默認200ms)。G1會嘗試調整堆分區(Region)大小和回收策略以達到該目標,但不保證嚴格滿足**。這個和上面說的可預測時間模型是有關系的,如果設置的太短那么回收Region選擇的就會很少,如果設置的太大又會讓用戶覺得卡頓。 - ?**
-XX:GCPauseTimeInterval
**?:設置GC停頓的間隔時間目標(毫秒),與MaxGCPauseMillis
配合使用。字面意思就是下一次GC相隔的時間。
?堆與Region配置?:
- ?**
-XX:G1HeapRegionSize
**?:設置Region大小(1MB~32MB,需為2的冪)。默認值由堆大小自動計算(所以到Region都是相同的,設置了后直到程序結束這個過程中Region的空間是不會改變的,另外G1回收器還有個Humongous區域就是存大對象的,超過1.5個Region就放在Humongous中,而這個H區是連續的不像R一樣分成很細的區域,不然一個大對象豈不是沒地方放了),大Region可減少碎片但可能增加停頓時間,小的話就是相反咯增加內存碎片但是減少停頓時間。 - ?**
-XX:InitiatingHeapOccupancyPercent
(IHOP)?**?:觸發并發標記周期的堆占用閾值(默認45%)。當內存占用超過此比例時,啟動混合GC。 - **
-XX:G1ReservePercent
**?:保留堆空間的百分比(默認10%),用于晉升失敗時的回退,避免Full GC。
?GC觸發與并行度?:
- **
-XX:ConcGCThreads
**?:并發標記階段的線程數(默認值基于CPU核心數)。過多線程可能影響應用吞吐量。 - ?**
-XX:ParallelGCThreads
**?:STW階段(如年輕代GC)的并行線程數,默認與CPU核心數相關。 - **
-XX:G1MixedGCCountTarget
**?:混合GC的預期回收次數(默認8次),控制老年代Region的回收節奏。 - ?**
-XX:G1HeapWastePercent
**?:允許的堆浪費比例(默認5%)。當可回收空間低于此值時,停止混合GC。
?還有很多的命令這邊就不一個一個去解釋介紹了,有興趣的小伙伴可以JVM實戰-G1參數調優 - 魚007 - 博客園自行查看了解。但是G1的設計初衷就是為了簡化JVM的調優,一般的調整只需要三步,啟用G1回收器,設置堆的最大內存,設置最大時間。
回收器和回收模式
不知道小伙伴還記不記得主包之前有提過的什么MinorGC、MajorGC、FullGC呢(JVM學習日記(二)Day2-CSDN博客)?那他們和主包上一篇講的7大GC有什么關系呢?
其實很好理解他們的關系就是回收器和回收模式,像MinorGC指的就是回收年輕代而Serial指的就是回收器,也就是MinorGC是所以年輕代GC的代名詞,而實際怎么去實現GC用什么算法是GC回收器決定的(這里提一下還有個MixedGC也就是混合GC,也是G1可以使用的,簡單來說就是同時回收年輕代和老年代,但是這個是部分回收,不像FullGC全部回收)。
記憶集與寫屏障
記憶集(Remembered Set, RSet)
每個Heap Region(內存分區)都有一個RSet,用于記錄其他Region中的對象對本Region的引用。例如:老年代Region A中的對象引用了年輕代Region B中的對象,則Region B的RSet會記錄這個引用關系(Rset記錄的是誰引用了我,而不是我引用了誰)。為什么需要RSet???G1是基于Region的分區回收器,回收時需要知道哪些Region是存活的(避免誤回收)。如果沒有RSet,每次回收一個Region時,需要掃描整個堆才能確定是否有其他Region引用它,效率極低。RSet讓G1可以只掃描相關Region,大幅減少GC工作量。sRSet如何實現??通常是一個哈希表或卡表(Card Table)?結構,存儲引用來源的Region或卡頁(Card)信息。例如:G1使用?“卡表”??(Card Table)來粗粒度記錄跨Region引用,再通過RSet細化管理。那什么又是卡表?將內存劃分為固定大小的卡頁,每個卡頁對于卡表中的一個標記位,簡單來說就是RegionA引用了RegionB中某一塊內存的對象,RegionA的Rset就會記錄這個被引用內存的卡頁。
寫屏障(Write Barrier)??
在對象引用被修改時觸發,記錄跨Region引用的變化到RSet。例如:當對象A(在Region 1)的字段被修改,指向對象B(在Region 2)時,寫屏障會更新Region 2的RSet。為什么需要寫屏障???在并發標記階段,用戶線程可能修改對象引用關系,導致標記結果不準確(漏標或誤標)。寫屏障通過捕獲引用變化,保證RSet和標記結果的正確性。寫屏障也分前/后寫屏障,簡單來說就是修改被引用對象的前/后修改引用他的Rset。
結構大概就是這樣子,這個的外R地址指的是外部Region的地址哦,這個ABCDE就是這個Region中存放的區塊。
外部Region地址 | 臟卡頁索引 | 實際引用位置 |
---|---|---|
0x10000000 | Bitmap:?...0100 | Region 1的卡頁2(0x10001000) |
0x30000000 | Bitmap:?...100000 | Region 3的卡頁5(0x30002800) |
?G1回收器的回收階段
?
1. 年輕代GC(Young GC)??
?觸發條件?:Eden區Region占滿時觸發(STW)。快速回收年輕代(Eden + Survivor)。關鍵步驟?:
- ?初始標記(Initial Mark)??:短暫STW,標記GC Roots直接可達的對象(借Young GC執行)。
- ?根區域掃描(Root Region Scanning)??:掃描Survivor區中引用老年代的對象(需在下次Young GC前完成)。
- ?復制存活對象(Evacuation)??:并行將存活對象復制到Survivor區或晉升到老年代。
- ?調整Region分配?:根據
MaxGCPauseMillis
動態調整Eden/Survivor的Region數量。
2. 并發標記周期(Concurrent Marking Cycle)??
- ?觸發條件?:老年代占用達到
InitiatingHeapOccupancyPercent
(默認45%)。標記全堆存活對象,為混合GC做準備。關鍵步驟?:
- ?初始標記(Initial Mark)??:短暫STW,標記GC Roots直接可達的對象(與Young GC共用)也就是先大概全堆標記一下確定范圍。
- ?并發標記(Concurrent Marking)??:短暫STW與用戶線程并發遍歷堆,標記存活對象。
- ?最終標記(Remark)??:短暫STW,處理并發標記期間遺漏的對象,可能上個階段并發的時候用戶線程又改變了部分對象,可能出現漏標。
- ?清理(Cleanup)??:統計Region存活數據,識別可回收Region(部分STW)。
?3. 混合回收(Mixed GC)??
- ?觸發條件?:并發標記完成后啟動。回收年輕代 + 部分老年代Region(存活率低的Region優先)。?關鍵步驟?:
- ?選擇回收集(Collection Set)??:包含所有年輕代Region + 部分老年代Region(基于
G1MixedGCLiveThresholdPercent,也就是對象存活率,默認65%當一個Region中存活的對象的內存占用小于這個Region的65%,這個區域會被優先收回
)。 - ?復制存活對象(Evacuation)??:并行將存活對象復制到空閑Region(STW)。
- ?調整策略?:根據
G1HeapWastePercent(允許可浪費空間,當可回收的垃圾小于這個默認值就不好采用混合GC)
決定是否繼續混合回收。
?4.FULLGC
這個就不多說啦,就是內存不足的時候無法使用上面的GC了就會觸發這個最后的GC,退回到單線程的Serial Old GC(完全STW,長時間停頓)。
總結
本篇主要講的就是G1回收器,本篇內容比較多,大火們慢慢品哦。