傳統分代 GC 的瓶頸
- **Full GC 停頓不可控**:大堆(>4GB)下停頓可達秒級,違背實時性要求(百毫秒級)。
- **並發收集器 (CMS) 缺陷**:整堆掃描、碎片整理困難、浮動垃圾佔 20% 預留空間、易發生
Concurrent Mode Failure 退化為串行 Full GC。
增量收集演進 → G1 (Garbage First)
核心思想:**將堆劃分為大小固定的 Region(默認 1-32MB,共 2048 個)**,按 Region 為單位回收,優先回收**垃圾密度最高**的 Region → **Garbage First**。
G1 設計目標
- 支持**超大堆**(十幾 GB 到百 GB)。
- **高吞吐**:多 CPU 並行/並發收集。
- **準實時**:可配置「N 毫秒內 GC 佔用 ≤ M 毫秒」。
- 代價:輕微吞吐損失換取停頓可控。
堆佈局與 Region 分類
[Eden Region] [Eden Region] [Survivor Region] [Old Region] [Old Region] ...
↑ 年輕代 ↑ ↑ 老年代 ↑
- **不再物理連續劃分**新生代/老年代,Region 動態分配角色。
- **Humongous Region**:專存超大對象(>50% Region 大小),直接歸老年代管理。
四階段回收流程
| 階段 | 動作 | 停頓 | 關鍵點 |
|------|------|------|--------|
| **1. 初始標記** | 掃描 GC Root → 標記直接可達對象,記錄 TAMS (Top at Mark Start) | **STW** | 極快,借用 Young GC 同步執行 |
| **2. 並發標記** | 從 Root 遍歷對象圖,識別存活對象;應用線程並發修改記入 **RSet (Remembered Set)** | 並發 | RSet 記錄跨 Region 引用,避免全堆掃描 |
| **3. 最終標記** | 處理 RSet 中剩餘修改,完成標記 | **STW** | 並行多線程處理,通常較短 |
| **4. 篩選回收** | **按存活對象大小排序 Region**,優先回收垃圾最多 Region;復制存活對象到新 Region,更新引用 | **STW** | 根據「目標停頓時間」決定回收多少 Region |
關鍵創新點
- Region 級別回收:不必全堆掃描,停頓時間可預估、可控。
- RSet (Remembered Set):每個 Region 維護「誰引用我」,跨 Region 引用增量更新,掃描只需看 RSet,不掃全堆。
- 混合收集:Young GC 同時回收部分 Old Region,避免老年代積壓觸發 Full GC。
- 雙向複製清理碎片:回收時將存活對象復制到新 Region,**天然整理碎片**,無需額外壓縮階段。
關鍵參數
-XX:+UseG1GC:啟用 G1(JDK 9+ 默認)。
-XX:MaxGCPauseMillis=200:目標最大停頓(默認 200ms)。
-XX:G1HeapRegionSize=16m:Region 大小(1-32MB,2 的冪)。
-XX:InitiatingHeapOccupancyPercent=45:老年代佔比達 45% 觸發並發標記。
展望
JDK 12+ 引入 **ZGC**、JDK 11+ **Shenandoah**,目標**亞毫秒級停頓**,不再依賴分代假設。未來調優將更聚焦於**業務延遲分位點**而非 GC 參數微調。