G1(Garbage-First)垃圾收集器

198 阅读5分钟

G1垃圾收集器通过Region分区和混合回收机制,平衡吞吐与延迟,支持大堆内存。利用SATB算法和记忆集减少停顿,适用延迟敏感场景。调优可控停顿,但需注意内存占用与Full GC风险。


一、G1 的设计目标

G1 是 JDK 7 引入的面向服务端应用的垃圾收集器,核心目标为:

  1. 可预测的停顿时间:通过设置 -XX:MaxGCPauseMillis(默认 200ms)控制 GC 停顿时间。
  2. 大堆内存友好:支持 TB 级堆内存,避免传统分代收集器的全堆扫描问题。
  3. 平衡吞吐与延迟:在较高吞吐量和较低延迟之间取得平衡。

二、G1 的核心设计

1. Region 分区

  • 堆划分:将堆内存划分为多个大小相等的 Region(默认约 2048 个,每个 Region 大小 1MB~32MB,通过 -XX:G1HeapRegionSize 指定)。

  • 动态分代

    • Region 可以是 EdenSurvivorOldHumongous(大对象区,存储大小超过 Region 50% 的对象)。
    • 分代逻辑上存在,物理上不连续,动态分配 Region 用途。

2. SATB(Snapshot-At-The-Beginning)算法

  • 并发标记基础:在标记开始时对堆内存“拍照”,记录存活对象快照。
  • 处理并发修改:通过写屏障(Write Barrier)记录并发期间被修改的引用,确保标记准确性。

3. Remembered Set(记忆集)

  • 跨 Region 引用跟踪:每个 Region 维护一个记忆集(RSet),记录其他 Region 对当前 Region 的引用。
  • 避免全堆扫描:GC 时仅需扫描 RSet,而非整个堆,提升效率。

三、G1 的工作流程

G1 的垃圾回收分为两个阶段:

  1. Young GC:回收年轻代(Eden + Survivor)的 Region。
  2. Mixed GC:混合回收年轻代和部分老年代 Region(需满足阈值条件)。

1. Young GC 流程

  1. 触发条件:Eden 区 Region 耗尽时触发。

  2. 复制存活对象

    • 将 Eden 和 Survivor 区存活对象复制到新的 Survivor Region。
    • 年龄计数器递增,达到阈值(默认 15)的对象晋升到 Old Region。
  3. 更新 RSet:记录跨 Region 引用。

2. Mixed GC 流程

  1. 触发条件

    • 堆内存使用率达到阈值(默认 45%,通过 -XX:InitiatingHeapOccupancyPercent 调整)。
    • 需要回收老年代 Region 以释放空间。
  2. 全局并发标记

    • 初始标记(Initial Mark,STW):标记 GC Roots 直接可达的对象(与 Young GC 同步触发)。
    • 根区域扫描(Root Region Scanning):扫描 Survivor Region 的引用。
    • 并发标记(Concurrent Mark):遍历堆,标记所有存活对象。
    • 最终标记(Final Mark,STW):处理 SATB 记录,修正标记结果。
    • 清理阶段(Cleanup,STW):统计各 Region 的存活对象比例,选择回收价值高的 Region。
  3. 混合回收(Mixed Collection,STW):

    • 回收选中的年轻代和老年代 Region。
    • 重复多次 Mixed GC 直到满足堆空间释放目标。

四、G1 的关键优化机制

1. 增量回收

  • 分批次回收:每次 Mixed GC 仅回收部分 Region,避免一次性全堆回收的长停顿。
  • 停顿可控:通过 -XX:MaxGCPauseMillis 动态调整回收 Region 数量。

2. 并行与并发

  • 并行阶段:Young GC 和 Mixed GC 的 STW 阶段使用多线程加速。
  • 并发阶段:全局并发标记的大部分操作与应用线程并发执行。

3. 内存整理

  • 局部整理:回收 Region 时,将存活对象复制到空闲 Region,减少内存碎片。
  • Full GC 兜底:当 Mixed GC 无法满足内存需求时,触发 Full GC(标记-整理算法,需避免)。

五、G1 的优缺点

优点

  • 可预测停顿:通过参数控制目标停顿时间。
  • 大堆高效:Region 分区减少全堆扫描开销。
  • 内存碎片少:局部整理降低碎片化风险。

缺点

  • 内存占用高:维护 RSet 和卡表占用额外内存(约堆的 10%~20%)。
  • 吞吐量略低:相比 Parallel 回收器,吞吐量降低约 10%。
  • Full GC 风险:并发标记失败或晋升失败时触发 Full GC。

六、G1 调优参数

参数作用示例值
-XX:+UseG1GC启用 G1 回收器默认不启用
-XX:MaxGCPauseMillis目标最大停顿时间(毫秒)200(默认)
-XX:InitiatingHeapOccupancyPercent触发 Mixed GC 的老年代占用阈值(百分比)45(默认)
-XX:G1HeapRegionSize指定 Region 大小16m(16MB)
-XX:G1NewSizePercent年轻代初始占比(堆百分比)5(默认)
-XX:G1MaxNewSizePercent年轻代最大占比(堆百分比)60(默认)
-XX:ConcGCThreads并发标记阶段的线程数根据 CPU 核数调整

七、适用场景

  1. 大堆内存应用:堆内存超过 6GB,传统分代收集器效率下降。
  2. 延迟敏感场景:需避免长时间停顿(如实时交易系统)。
  3. 内存碎片问题:老年代碎片化导致 Full GC 频繁。

八、G1 与其他回收器的对比

回收器停顿时间吞吐量内存占用适用场景
G1可控(亚秒级)中等较高大堆、平衡吞吐与延迟
CMS低(但碎片导致不稳定)中等老年代低延迟,堆较小
Parallel吞吐优先,堆较小
ZGC极低(亚毫秒级)中等超大堆(TB 级)、极致低延迟

九、总结

G1 通过 Region 分区SATB 标记混合回收 机制,实现了大堆内存下的高效垃圾回收,兼顾吞吐量和延迟。调优时需关注目标停顿时间、堆占用阈值和 Region 大小,结合监控工具(如 GC 日志、JVisualVM)分析瓶颈。对于追求极致低延迟的超大堆场景,可考虑 ZGC 或 Shenandoah,但 G1 仍是大多数生产环境的稳健选择。