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年:持续优化大内存场景下的性能
重要技术演进
-
Region内存管理
- 从固定大小发展到动态调整
- 引入Humongous区域
- 优化Region回收策略
-
回收算法优化
- 引入增量回收
- 改进并发标记
- 优化重分配过程
-
性能提升历程
- 降低RSet维护开销
- 提高并发效率
- 优化停顿时间预测
应用实践的发展
-
早期阶段(2011-2014)
- 主要在中小规模应用中试用
- 积累生产环境经验
- 解决稳定性问题
-
推广阶段(2015-2017)
- 大规模应用开始采用
- 形成最佳实践指南
- 建立性能调优方法
-
全面应用阶段(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 // 设置并发线程数
高级参数
-XX:InitiatingHeapOccupancyPercent=45 // 启动标记周期的堆占用阈值
-XX:G1MixedGCLiveThresholdPercent=85 // Mixed GC时Region存活对象阈值
-XX:G1HeapWastePercent=5 // 触发混合垃圾回收的堆废物百分比
G1的最佳实践
1. 内存大小设置
- 建议堆内存6GB以上
- Region大小最好是2的幂次方
- 避免过多的Humongous对象
2. 停顿时间调优
- 合理设置MaxGCPauseMillis
- 监控实际停顿时间
- 适当调整并行线程数
3. 监控指标
- Region的使用状况
- 混合垃圾回收的频率
- Humongous区域的使用情况
- RSet的大小变化
常见问题与解决方案
1. 内存碎片问题
- 原因:大对象分配和Region回收导致
- 解决:调整Region大小和Humongous阈值
2. 停顿时间不稳定
- 原因:垃圾分布不均匀或对象分配速率变化
- 解决:调整混合回收参数和启动阈值
3. CPU使用率高
- 原因:并发标记和RSet维护开销
- 解决:调整并发线程数和RSet大小
性能对比
与CMS的比较
-
优势:
- 可预测的停顿时间
- 更好的空间整理能力
- 更少的内存碎片
-
劣势:
- 内存占用略高
- CPU开销较大
与ZGC的比较
-
优势:
- 更广泛的平台支持
- 更成熟的生产环境验证
- 更低的内存开销
-
劣势:
- 停顿时间相对较长
- 对大内存支持不如ZGC
总结
G1收集器通过其独特的Region内存布局和可预测的停顿时间目标,很好地平衡了吞吐量和延迟。它特别适合需要大内存的服务端应用,同时也为未来的垃圾收集器发展指明了方向。
参考资料
- Oracle G1 GC官方文档
- 《深入理解Java虚拟机》- 周志明
- JVM性能调优指南
- OpenJDK G1源码文档