G1 回收过程/优化建议
G1 的完整回收过程
G1 的收集过程比传统 GC 更复杂,分为多个阶段:
G1 完整收集周期:
┌────────────────────────────────────────────────────┐
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ 1. 年轻代收集(Young GC) │ │
│ │ - Eden Region → Survivor Region │ │
│ │ - STW(短暂停顿) │ │
│ └──────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────┴─────────────┐ │
│ ▼ ▼ │
│ ┌────────────────────┐ ┌────────────────────┐ │
│ │ 2a. 并发标记周期 │ │ 2b. 年轻代收集 │ │
│ │ (Initial Mark) │ │ (继续执行) │ │
│ │ - STW(短暂) │ │ - STW(短暂) │ │
│ └────────────────────┘ └────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────┐ │
│ │ 3. 混合收集(Mixed GC) │ │
│ │ - 年轻代 Region + 老年代 Region │ │
│ │ - 若干次混合收集 │ │
│ │ - STW(可预测停顿) │ │
│ └──────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────┐ │
│ │ 4. 重新标记/清理(Remark/Cleanup) │ │
│ │ - 短暂 STW │ │
│ │ - 计算最终 CSet │ │
│ └──────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 返回 1. 年轻代收集 │
│ │
└────────────────────────────────────────────────────┘年轻代收集(Young GC)
详细过程
Young GC 执行过程:
1. 阶段:选择收集集合(CSet)
└─ 选定所有 Eden Region 和 Survivor Region
2. 阶段:多线程并行复制
└─ 把 Eden 和 Survivor 中的存活对象
复制到新的 Survivor 和 Old Region
3. 阶段:更新 RSet
└─ 更新被引用的 Region 的 RSet
4. 阶段:释放空 Region
└─ 被清空的 Region 变成 Free RegionYoung GC 的日志
2026-03-22T10:00:00.123: [GC pause (G1 Evacuation Pause) (young)
Using 8 workers
, 0.0123456 secs]
[Eden: 1536.0M(1536.0M)->0.0B
Survivors: 96.0M->128.0M
Heap: 4096.0M(4096.0M)->2048.0M(4096.0M)]并发标记周期
G1 的并发标记周期是它最重要的特性之一:
并发标记周期:
1. 初始标记(Initial Mark)── STW
└─ 标记从 Survivor Region 晋升的对象
2. 根区域扫描(Root Region Scan)── 并发
└─ 扫描 Survivor Region 中的对象引用
3. 并发标记(Concurrent Mark)── 并发
└─ 从 GC Roots 出发,标记所有存活对象
4. 重新标记(Remark)── STW
└─ 修正并发期间的引用变化(SATB 快照)
5. 清理(Cleanup)── 短暂 STW
└─ 计算每个 Region 的回收价值SATB:G1 的标记算法
G1 使用 SATB(Snapshot-At-The-Beginning) 算法解决并发标记期间的对象消失问题:
SATB 的原理:
GC 开始时,对所有跨 Region 的引用做快照
↓
并发标记期间,对象被删除引用 → 记录到「待处理列表」
↓
Remark 时,所有在快照中被标记但现在不可达的对象
被标记为存活
↓
这些「快照中存活但实际上已死亡」的对象
被称为「浮动垃圾」浮动垃圾
浮动垃圾(Floating Garbage):
并发标记期间产生的垃圾,下次 GC 时才能回收:
1. 并发标记时,对象 X 被标记为存活
2. 标记完成后,用户线程清除了对 X 的引用
3. X 现在是垃圾,但已标记完成
4. 必须等到下一次 GC 才能回收 X混合收集(Mixed GC)
Mixed GC 的触发
当满足以下条件时,G1 触发混合收集:
触发条件:
老年代使用率 > InitiatingHeapOccupancyPercent
(默认 45%)Mixed GC 的过程
Mixed GC 执行过程:
1. 选定 CSet:
├─ 所有年轻代 Region
└─ 若干老年代 Region(按回收价值排序)
2. 多线程并行收集:
├─ 年轻代 Region → 复制到 Survivor/Old
└─ 老年代 Region → 标记-清除(或复制到其他 Region)
3. 清理:释放空 Region混合收集次数
G1 会执行多次混合收集,直到满足条件:
bash
# 一次混合收集周期最多多少次混合 GC
-XX:G1MixedGCCountTarget=8 # 默认G1 优化建议
优化一:设置合理的停顿目标
bash
# 不要设置过小的停顿目标(否则 G1 无法完成任务)
java -XX:MaxGCPauseMillis=50 MyApp # 太激进,GC 无法完成
# 合理的范围:100~500ms
java -XX:MaxGCPauseMillis=200 MyApp # 推荐优化二:增大堆或年轻代
如果年轻代收集太频繁:
bash
# 增大年轻代,减少 MinorGC 频率
java -Xmn2g -XX:+UseG1GC MyApp
# 或者增大总堆大小
java -Xms8g -Xmx8g -XX:+UseG1GC MyApp优化三:调整混合收集次数
如果混合收集次数太多(影响吞吐量):
bash
# 减少混合收集次数
java -XX:G1MixedGCCountTarget=4 MyApp
# 提高混合收集阈值
java -XX:G1MixedGCLiveThresholdPercent=90 MyApp优化四:减少 Humongous 对象
Humongous 对象(> Region 大小 50%)分配效率低:
bash
# 如果大量使用大数组,增大 Region 大小
java -XX:G1HeapRegionSize=8m MyApp
# 或者调整 Humongous 阈值
# (但这不是公开参数)G1 的 GC 日志分析
bash
# 完整的 G1 GC 日志
java -Xms4g -Xmx4g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:+PrintGCDetails \
-XX:+PrintGCDateStamps \
-XX:+PrintTenuringDistribution \
MyApp日志解读
# 年轻代收集
2026-03-22T10:00:00.000: [GC pause (G1 Evacuation Pause) (young)
[Eden: 2048.0M(2048.0M)->0.0B
Survivors: 128.0M->256.0M
Heap: 4096.0M(4096.0M)->2560.0M(4096.0M)]
0.0456789 secs]
# 并发标记
2026-03-22T10:00:30.000: [GC pause (G1 Evacuation Pause) (young)
2026-03-22T10:00:30.100: [GC concurrent-root-region-scan-end, 0.0012345 secs]
2026-03-22T10:00:30.200: [GC concurrent-mark-end, 0.0987654 secs]
# 混合收集
2026-03-22T10:00:31.000: [GC pause (G1 Evacuation Pause) (mixed)
[Eden: 0.0B(0.0B)->0.0B
Survivors: 256.0M->128.0M
Heap: 4096.0M(4096.0M)->2048.0M(4096.0M)]
[Collection Sets: 512.0M selected]
0.0789012 secs]本节小结
G1 回收过程与优化:
| 阶段 | STW | 说明 |
|---|---|---|
| Young GC | 是 | 年轻代 Region 复制 |
| Initial Mark | 是 | 标记 Survivor 晋升的对象 |
| Concurrent Mark | 否 | 并发标记存活对象 |
| Remark | 是 | 修正并发期间的引用变化 |
| Mixed GC | 是 | 年轻代 + 老年代 Region 收集 |
| Cleanup | 是(短暂) | 计算回收价值,释放空 Region |
G1 优化的核心原则:
- 设置合理的
MaxGCPauseMillis(不要过小) - 增大年轻代减少 MinorGC 频率
- 增大堆或调整 NewRatio 改善吞吐量
- 监控 GC 日志,根据实际情况调整
理解 G1 的回收过程,是进行精细调优的基础。
到这里,G1 专题全部完成。接下来我们来看 新型回收器(Epsilon/Shenandoah/ZGC)。
