跳到主要内容

G1垃圾收集器:新一代服务端垃圾回收方案

· 阅读需 8 分钟

什么是G1垃圾收集器?

G1(Garbage First)收集器是在JDK 7中首次引入,并在JDK 9中成为默认垃圾收集器的一种面向服务端应用的垃圾收集器。它的设计目标是为了取代CMS收集器,同时兼顾吞吐量和停顿时间,特别适合堆内存大小在6GB及以上的应用程序。

G1收集器的发展历史

早期设计阶段(2004-2007)

  • 2004年:Sun公司开始G1项目的研发
  • 2005年:提出Region化内存布局的设计理念
  • 2006年:完成Remember Sets的初步设计
  • 2007年:实现首个原型版本

实验特性阶段(2008-2011)

  • 2008年:在JDK 7的早期版本中作为实验特性引入
  • 2009年:增加了自适应堆调整算法
  • 2010年:改进了大对象(Humongous Objects)的处理
  • 2011年:在OpenJDK 7中发布第一个正式版本

成熟发展阶段(2012-2016)

  • 2012年:JDK 7 Update 4,G1进入生产环境推荐使用阶段
  • 2014年:增强了Mixed GC的性能
  • 2015年:优化了字符串去重功能
  • 2016年:改进了SATB(Snapshot-At-The-Beginning)算法

默认收集器阶段(2017至今)

  • 2017年:在JDK 9中正式成为默认垃圾收集器
  • 2018年:JDK 10引入了并行Full GC
  • 2019年:JDK 12优化了Abort Out Of Memory机制
  • 2021年:JDK 16引入了分代式G1收集器
  • 2023年:持续优化大内存场景下的性能

重要技术演进

  1. Region内存管理

    • 从固定大小发展到动态调整
    • 引入Humongous区域
    • 优化Region回收策略
  2. 回收算法优化

    • 引入增量回收
    • 改进并发标记
    • 优化重分配过程
  3. 性能提升历程

    • 降低RSet维护开销
    • 提高并发效率
    • 优化停顿时间预测

应用实践的发展

  1. 早期阶段(2011-2014)

    • 主要在中小规模应用中试用
    • 积累生产环境经验
    • 解决稳定性问题
  2. 推广阶段(2015-2017)

    • 大规模应用开始采用
    • 形成最佳实践指南
    • 建立性能调优方法
  3. 全面应用阶段(2018至今)

    • 成为主流生产环境的首选
    • 建立完善的监控体系
    • 形成成熟的调优方案

G1的技术特点

1. 内存布局

  • Region化的堆内存
    • 堆空间被均匀分割成多个大小相等的Region
    • 每个Region大小通常为1MB到32MB
    • Region可以动态担任Eden、Survivor、Old区角色
    • 特殊的Humongous区域用于存储大对象

2. 核心概念

  • Remember Sets(RSet)

    • 用于记录Region间对象引用关系
    • 避免全堆扫描
    • 每个Region都维护自己的RSet
  • Collection Sets(CSet)

    • 需要被回收的Region集合
    • 在GC时会被完全清理
    • 包含垃圾对象最多的Region优先回收

3. 工作流程

初始标记(Initial Mark)

  • 标记GC Roots直接关联的对象
  • 需要STW(Stop The World),但时间很短

并发标记(Concurrent Mark)

  • 从GC Roots开始对堆中对象进行可达性分析
  • 与应用程序并发执行
  • 计算每个Region的存活对象情况

最终标记(Final Mark)

  • 处理并发标记阶段遗留的SATB记录
  • 需要短暂的STW暂停

筛选回收(Live Data Counting and Evacuation)

  • 对各个Region的回收价值和成本进行排序
  • 根据用户期望的停顿时间来制定回收计划
  • 选定CSet中的Region进行回收

G1的优势特点

1. 可预测的停顿时间

  • 支持设置预期停顿时间(-XX:MaxGCPauseMillis
  • 根据历史数据动态调整回收策略
  • 优先回收价值最大的Region

2. 空间整理

  • 标记-复制算法,实现内存整理
  • 减少内存碎片
  • 大对象分配更便捷

3. 并发收集

  • 大部分工作与应用线程并发执行
  • 仅在必要时才执行STW操作
  • 停顿时间相对较短且可控

G1的调优参数

基础参数

-XX:+UseG1GC                     // 启用G1收集器
-XX:MaxGCPauseMillis=200 // 期望的最大停顿时间
-XX:G1HeapRegionSize=n // 设置Region大小
-XX:ParallelGCThreads=n // 设置并行线程数
-XX:ConcGCThreads=n // 设置并发线程数
java

高级参数

-XX:InitiatingHeapOccupancyPercent=45  // 启动标记周期的堆占用阈值
-XX:G1MixedGCLiveThresholdPercent=85 // Mixed GC时Region存活对象阈值
-XX:G1HeapWastePercent=5 // 触发混合垃圾回收的堆废物百分比
java

G1的最佳实践

1. 内存大小设置

  • 建议堆内存6GB以上
  • Region大小最好是2的幂次方
  • 避免过多的Humongous对象

2. 停顿时间调优

  • 合理设置MaxGCPauseMillis
  • 监控实际停顿时间
  • 适当调整并行线程数

3. 监控指标

  • Region的使用状况
  • 混合垃圾回收的频率
  • Humongous区域的使用情况
  • RSet的大小变化

常见问题与解决方案

1. 内存碎片问题

  • 原因:大对象分配和Region回收导致
  • 解决:调整Region大小和Humongous阈值

2. 停顿时间不稳定

  • 原因:垃圾分布不均匀或对象分配速率变化
  • 解决:调整混合回收参数和启动阈值

3. CPU使用率高

  • 原因:并发标记和RSet维护开销
  • 解决:调整并发线程数和RSet大小

性能对比

与CMS的比较

  1. 优势:

    • 可预测的停顿时间
    • 更好的空间整理能力
    • 更少的内存碎片
  2. 劣势:

    • 内存占用略高
    • CPU开销较大

与ZGC的比较

  1. 优势:

    • 更广泛的平台支持
    • 更成熟的生产环境验证
    • 更低的内存开销
  2. 劣势:

    • 停顿时间相对较长
    • 对大内存支持不如ZGC

总结

G1收集器通过其独特的Region内存布局和可预测的停顿时间目标,很好地平衡了吞吐量和延迟。它特别适合需要大内存的服务端应用,同时也为未来的垃圾收集器发展指明了方向。

参考资料

  1. Oracle G1 GC官方文档
  2. 《深入理解Java虚拟机》- 周志明
  3. JVM性能调优指南
  4. OpenJDK G1源码文档