G1 (Garbage First) 作為 JDK 9+ 默認收集器,面向大堆 (4GB-32GB+) 低延遲場景。本文系統剖析 Region 分區架構、Young/Mixed GC 流程、SATB 並發標記、RSet/CTable 記憶集、關鍵調優參數與 Full GC 觸發場景應對。
| 目標 | 傳統收集器痛點 | G1 解決方案 |
| 可預測停頓 | CMS 停頓不可控、Full GC 災難性 | 目標停頓時間 MaxGCPauseMillis,自適應調整收集集合 |
| 大堆支持 | CMS 碎片導致大堆不可用 | Region 分區 + 標記整理(局部復制),無碎片 |
| 高吞吐 | Serial/ParNew 單線程/並行但 STW 長 | 多線程並行 + 並發標記 + 增量收集 |
二、Region 分區架構
堆內存 (連續虛擬地址空間)
┌─────────────────────────────────────────────┐
│ Region 0 │ Region 1 │ Region 2 │ ... Region N │ ← 2048 個大小相等區域 (1-32MB, 2的冪)
├─────────────────────────────────────────────┤
│ 邏輯分代 (非物理連續) │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Eden │ │Survivor │ │ Old │ ... │ ← 每個 Region 標記邏輯代
│ │ Region │ │ Region │ │ Region │ │
│ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────┘
- Region 大小:啟動時根據堆大小計算,目標 ~2048 個 Region,
-XX:G1HeapRegionSize 可指定
- Humongous Region:對象 > 50% Region Size 直接分配為巨型對象,佔用連續多個 Region,默認進老年代
- 可用空間:Free Region 鏈表管理,分配優先從 Free 獲取
三、記憶集:RSet 與 Card Table
3.1 核心問題
Young GC 掃描 Roots 時,老年代 → 新生代引用若全掃描老年代則極慢。需記錄「哪些老年代 Region 引用了新生代 Region」。
3.2 Card Table (卡表)
- 將 Region 劃分為 512B Card (默認)
- 字節數組
byte[] cardTable,索引映射 Card,值 0=清潔/1=髒
- 寫屏障:引用賦值時標記目標 Card 為髒
3.3 RSet (Remembered Set)
- 每個 Region 維護一個 RSet:
Map<Region, Set<CardIndex>>
- Key:來源 Region 起始地址;Value:該 Region 中髒 Card 索引集合
- Young GC 時:只需遍歷 RSet 中的 Card,掃描其中引用,避免全老年代掃描
Point-in vs Point-out:G1 採用 Point-in (誰引用我),CMS 採用 Point-out (我引用誰)。Point-in 更適合 Region 眾多場景。
四、Young GC 流程 (STW)
- 根掃描:掃描線程棧、JNI Handles、JVMTI 等 GC Roots
- 更新 RSet:處理 Dirty Card Queue,更新 RSet
- 處理 RSet:遍歷 RSet,掃描老年代→新生代引用,標記存活對象
- 對象復制/遷移:存活對象復制到 Survivor/Old Region (復制算法,隱含整理)
- 引用處理:軟/弱/虛/終結器引用處理
CSet (Collection Set):本次 GC 目標 Region 集合。Young GC = 所有 Eden + Survivor Region。
五、並發標記與 Mixed GC (核心創新)
5.1 全局並發標記 5 階段
| 階段 | 類型 | 核心工作 | STW? |
| 初始標記 | STW | 掃描 GC Roots,標記直接可達對象 (伴隨 Young GC) | 是 |
| 根區域掃描 | 並發 | 掃描初始標記存活 Region 中的老年代引用 | 否 |
| 並發標記 | 並發 | 遍歷對象圖,標記所有可達對象 (可被 Young GC 中斷) | 否 |
| 最終標記 | STW | 處理 SATB Buffer,修正並發階段變更,多線程並行 | 是 |
| 清理 | STW+並發 | 統計 Region 活度,識別空 Region、混合收集候選 Region,重置 RSet | 部分 |
5.2 SATB (Snapshot-At-The-Beginning) 寫屏障
// 伪代碼:寫屏障邏輯
void preWriteBarrier(Object field) {
if (markingInProgress) {
Object oldValue = field.get();
if (oldValue != null && !isMarked(oldValue)) {
satbQueue.add(oldValue); // 記錄舊引用對象,防止漏標
}
}
}
- 核心思想:並發標記開始時活著的對象「快照」必須存活,即使引用斷開也標記為灰色
- 三色不變式:強三色不變式 (CMS 增量更新) vs 弱三色不變式 (G1 SATB)
- 浮動垃圾:並發階段新分配/引用斷開的對象本輪不回收,下輪處理
5.3 Mixed GC
- 觸發:老年代佔用達到
InitiatingHeapOccupancyPercent (默認 45%)
- CSet = 所有新生代 Region + 活度最高的若干老年代 Region
- 目標:在
MaxGCPauseMillis 內完成,優先回收垃圾密度高的 Region
六、對象分配策略
- TLAB:線程本地緩衝,Eden 中劃分,無鎖指針碰撞分配
- Eden 分配:TLAB 滿或對象過大,直接在 Eden 指針碰撞/空閒列表分配
- Humongous 分配:對象 > 50% Region,直接在老年代尋找連續 Region,可能觸發 Full GC
七、關鍵調優參數
| 參數 | 默認值 | 調優建議 |
-XX:+UseG1GC | - | 啟用 G1 (JDK 9+ 默認) |
-Xmx | - | 設定最大堆,生產建議 8-32GB |
-XX:MaxGCPauseMillis | 200ms | 核心手柄,業務可接受停頓上限,100-500ms 常見 |
-XX:G1HeapRegionSize | 自動 | 1-32MB 2的冪,大堆可調大 (16/32MB) 減少 Region 數 |
-XX:ParallelGCThreads | CPU核心數 | STW 工作線程,≤8 核用核心數,>8 用 5/8 |
-XX:ConcGCThreads | ParallelGCThreads/4 | 並發標記線程,可適當增加加速標記 |
-XX:InitiatingHeapOccupancyPercent | 45% | 提前觸發標記可減少 Full GC 風險,調 35-40% |
-XX:G1ReservePercent | 10% | 預留內存防疏散失敗,增大至 15-20% |
禁用參數:-Xmn、-XX:NewRatio、-XX:SurvivorRatio — 固定新生代破壞 G1 自適應。
八、Full GC 觸發場景與應對 (退化為 Serial Old)
| 場景 | 原因 | 日誌特徵 | 應對策略 |
| 併發模式失敗 | 標記未完成老年代已滿 | concurrent mode failure | 增大堆、降低 IHOP、增加並發線程 |
| 疏散/晉升失敗 | Survivor/Old 無空間容納存活對象 | to-space exhausted/overflow | 增大 ReservePercent、提前標記、增大堆 |
| 巨型對象分配失敗 | 找不到連續 Region | Humongous allocation failed | 避免巨型對象、增大 Region Size、增大堆 |
| 元空間不足 | Metaspace OOM | Metadata GC threshold exceeded | 增大 MaxMetaspaceSize、排查類加載洩漏 |
九、G1 vs CMS vs ZGC 選型指南
| 維度 | CMS | G1 | ZGC |
| JDK 版本 | 1.4+ (JDK 14 移除) | 7u4+ (9+ 默認) | 11+ (15+ 生產就緒) |
| 堆大小適用 | < 4-6GB | 4-32GB+ | 8GB - TB 級 |
| 停頓目標 | 低延遲 (非確定性) | 可預測 (目標導向) | 亞毫秒 (硬實時) |
| 碎片處理 | 標記清除 + Full GC 整理 | 標記整理 (局部復制) | 染色指針 + 重定位 (無碎片) |
| CPU 開銷 | 中等 | 較高 (RSet/SATB) | 高 (讀屏障/染色指針) |
| 遷移建議 | 停止維護,遷移 G1/ZGC | 主流穩健選擇 | 極低延遲、超大堆首選 |
- 上一篇文章: Java 邏輯運算符 & 與 && 核心區別:短路求值與副作用陷阱
- 下一篇文章: TF-IDF MapReduce 實現全流程:三階段作業鏈、IK 分詞、分布式緩存優化與精準營銷應用
Copyright ©
程式碼之家 All Rights Reserved