作为 JVM 垃圾收集器的里程碑式设计,G1 旨在替代 CMS,解决CMS内存碎片和不可控停顿等核心痛点,同时为现代大内存、多核处理器环境提供更平衡的吞吐量与低延迟表现。
一、核心设计思想
1. 分区(Region)
- 堆不再物理划分为固定的年轻代(Young)、老年代(Old),而是划分为 大小相等 的 Region(默认约 2048 个,每个 Region 大小为 1MB~32MB)。
- Region 类型动态变化:可随时作为 Eden、Survivor、Old 或 Humongous(存放大对象)区使用。
- 优势:
- 避免 CMS 的内存碎片问题:通过 复制算法(年轻代)和 标记-整理算法(老年代)在 Region 间移动对象。
- 更精细的内存管理:可针对部分 Region 回收,避免全堆扫描。
2. 停顿预测
- 核心目标:满足用户设定的 最大停顿时间目标(
-XX:MaxGCPauseMillis,默认 200ms)。 - 实现方式:
- 跟踪每个 Region 的回收价值(可释放空间)与回收成本(复制对象的时间)。
- 每次回收优先选择 垃圾最多(Garbage-First) 的 Region 集合,在有限时间内释放最大内存。
二、工作流程
1. 年轻代收集(Young GC)
- 触发条件:Eden 区占满时触发。
- 过程(STW):
- 扫描 GC Roots,标记 Eden 和 Survivor 区的存活对象。
- 存活对象复制到新的 Survivor Region(晋升)或 Old Region(达到年龄阈值)。
- 清空原有 Eden/Survivor Region,加入空闲列表。
- 特点:并行多线程执行,停顿时间较短。
2. 并发标记周期(Concurrent Marking)
目标:识别老年代 Region 中的垃圾,为混合收集做准备。
阶段(类似 CMS,但基于 Region):
- 初始标记(STW)
- 标记 GC Roots 直接关联的对象。
- 借道 Young GC:在 Young GC 的 STW 期间同步完成,成本极低。
- 根区域扫描
- 扫描 Survivor Region(根区域)到老年代的引用。
- 需在下次 Young GC 前完成(否则阻塞 Young GC)。
- 并发标记(Concurrent Mark)
- 遍历堆,标记所有存活对象(使用 SATB 算法 处理并发修改)。
- 最终标记(STW)
- 处理 SATB 缓冲区引用,修正并发标记期间的变动。
- 筛选回收(STW)
- 统计各 Region 存活对象占比,排序回收价值。
- 释放完全空闲的 Region,更新空闲列表。
3. 混合收集(Mixed GC)
- 触发条件:老年代占用达到阈值(
-XX:InitiatingHeapOccupancyPercent,默认 45%)。 - 过程:
- 选择一批 高收益的老年代 Region + 所有年轻代 Region 进行回收。
- 采用 复制算法:将存活对象移动到空闲 Region(整理内存)。
- 多次执行:直到回收足够内存或达到 MaxGCPauseMillis 目标。
4. Full GC(失败回退)
- 触发条件:
- 晋升失败(复制对象时目标 Region 不足)。
- 并发标记周期未完成时堆已占满。
- 行为:退化为单线程的 Serial Old GC(Mark-Compact),停顿极长!
→ 需优化避免 Full GC。
三、关键技术
1. SATB(Snapshot-At-The-Beginning)
- 解决并发标记漏标问题:
- 初始标记时对堆做快照,后续标记基于此快照。
- 并发期间被修改的引用记录到 SATB 缓冲区,最终标记阶段再扫描。
- 优势:比 CMS 的增量更新更高效,减少重新标记工作量。
2. 记忆集(Remembered Set, RSet)
- 问题:跨 Region 引用如何避免全堆扫描?
- 解决:
- 每个 Region 有一个 RSet,记录 其他 Region 指向本 Region 的引用。
- 写屏障(Write Barrier)监控引用修改,更新 RSet。
- 作用:回收时只需扫描 RSet,避免遍历整个老年代。
3. 收集集合(Collection Set, CSet)
- 每次 GC 回收的 Region 集合(Young GC 含 Eden+Survivor,Mixed GC 额外加入老年代 Region)。
- 选择策略:优先选择垃圾比例高(Garbage-First)、回收耗时低的 Region。
四、核心参数与调优
| 参数 | 说明 |
|---|---|
-XX:+UseG1GC | 启用 G1 收集器 |
-XX:MaxGCPauseMillis=200 | 目标最大停顿时间(软目标,非强制保证) |
-XX:InitiatingHeapOccupancyPercent=45 | 触发并发标记周期的堆占用阈值 |
-XX:G1HeapRegionSize=N | 手动设置 Region 大小(1MB~32MB,需为 2 的幂) |
-XX:G1NewSizePercent / -XX:G1MaxNewSizePercent | 年轻代占比范围(默认 5%~60%) |
-XX:G1MixedGCLiveThresholdPercent=85 | 混合收集中,Old Region 存活对象占比 低于此值 才加入 CSet |
-XX:G1MixedGCCountTarget=8 | 混合 GC 分几次执行完(避免单次停顿过长) |
-XX:G1HeapWastePercent=5 | 可容忍的堆浪费比例,达到则停止混合 GC |
调优方向:
- 优先调整
MaxGCPauseMillis:根据应用需求设定合理值(过低会导致频繁 GC)。 - 监控避免 Full GC:
- 增加堆大小或降低
InitiatingHeapOccupancyPercent。 - 优化对象分配速率(减少大对象、调整晋升年龄)。
- 增加堆大小或降低
- 关注 RSet 开销:过高的写屏障负担可能拖慢应用(通过
-XX:+G1SummarizeRSetStats分析)。
五、G1 vs CMS:核心优势对比
| 特性 | CMS | G1 |
|---|---|---|
| 内存布局 | 连续分代(Young/Old) | 分区模型(Region) |
| 算法 | 标记-清除(产生碎片) | 复制+标记-整理(无碎片) |
| 停顿控制 | 不可预测 | 可预测(软实时目标) |
| 大堆处理 | 性能下降明显 | 更适合大堆(4GB~数十GB) |
| Full GC 风险 | 高(碎片+并发失败) | 低(设计规避,仍有回退) |
| 适用版本 | Java 1.4~8(14 移除) | Java 7u4+,JDK 9+ 默认收集器 |
六、缺点
- 内存占用更高:
- RSet 和 Card Table 占用额外空间(约堆的 10%~20%)。
- 写屏障开销:
- 监控引用修改有 CPU 成本(尤其引用密集型应用)。
- 小堆不适用:
- Region 管理和元数据开销在小堆(<4GB)中占比过高。
七、优化与替代方案
- ZGC / Shenandoah:
新一代超低停顿(亚毫秒级)收集器,适用于 TB 级堆。核心创新:- ZGC:染色指针 + 并发压缩。
- Shenandoah:并发复制 + 读屏障。
- G1 仍在优化:
JDK 12+ 引入特性如:- 及时返回未用内存(
-XX:+G1UncommitDelay)。 - 并行 Full GC(JDK 10+,避免单线程停顿)。
- 及时返回未用内存(
总结
G1 是面向服务端应用的里程碑式垃圾收集器,它通过分区、停顿预测和并发标记-整理算法,在延迟可控性、吞吐量和堆容量之间取得了平衡:
- ✅ 解决 内存碎片 问题
- ✅ 提供 可预测的停顿时间
- ✅ 适应 大内存场景(4GB~数十GB)
224

被折叠的 条评论
为什么被折叠?



