Skip to content

G1 和 ZGC 垃圾回收器

JVM垃圾回收是Java平台的核心特性之一,负责自动管理内存,避免开发人员手动释放内存。

随着Java技术的发展,JVM垃圾回收器也经历了多代演进。下面我将介绍主要的垃圾回收器,并重点讲述G1和ZGC。

常见垃圾回收器

Serial收集器

  • 单线程收集器,工作时需要暂停所有应用线程(STW)
  • 简单高效,适用于单CPU环境和内存小于100MB的场景
  • 参数:-XX:+UseSerialGC

ParNew收集器

  • Serial收集器的多线程版本
  • 常与CMS一起使用作为年轻代收集器
  • 参数:-XX:+UseParNewGC

Parallel Scavenge/Parallel Old

  • 注重吞吐量的多线程收集器
  • 可以通过参数控制最大暂停时间或吞吐量
  • 参数:-XX:+UseParallelGC

CMS (Concurrent Mark Sweep)

  • 以获取最短回收停顿时间为目标的收集器
  • 采用标记-清除算法,并发进行大部分工作
  • 缺点:内存碎片、CPU资源敏感、浮动垃圾
  • 参数:-XX:+UseConcMarkSweepGC
  • JDK9标记为废弃,JDK14正式移除

问题: 为什么CMS垃圾回收器被废弃并移除

为什么CMS垃圾回收器被废弃并移除

G1垃圾回收器(Garbage-First)

基本概念

G1(Garbage-First)是Oracle JDK 7引入的垃圾回收器,设计目标是替代CMS收集器。在JDK 9中被设为默认垃圾收集器。

内存布局

G1将堆划分为大小相等的区域(Region),每个区域大小通常为1MB-32MB。区域有四种类型:

  • Eden区域:存放新分配对象
  • Survivor区域:存放幸存对象
  • Old区域:存放长期存活对象
  • Humongous区域:存放大对象(超过区域大小50%的对象)

这种设计带来了显著优势:不再严格划分新生代和老年代的物理空间,可以动态调整各代的大小。

工作流程

G1收集器的工作过程可分为以下几个阶段:

  1. 初始标记(Initial Marking):STW,标记GC Roots直接关联对象
  2. 并发标记(Concurrent Marking):与应用线程并发执行,标记整个堆中的存活对象
  3. 最终标记(Final Marking/Remark):STW,处理并发标记阶段遗留的标记任务
  4. 筛选回收(Cleanup):STW,对各个区域的回收价值和成本进行排序,根据用户期望的停顿时间制定回收计划,然后回收选定的区域

G1的核心特性

  1. 预测停顿模型:G1会根据历史数据预测每个区域的回收时间,优先回收价值最高(回收空间最大)的区域,实现用户设定的停顿目标(-XX:MaxGCPauseMillis)
  2. 增量式垃圾回收:G1不需要一次性回收整个堆空间,而是每次只处理部分区域
  3. 混合式回收(Mixed GC):G1可以同时回收年轻代和部分老年代,避免了完整的老年代GC
  4. SATB(Snapshot-At-The-Beginning)算法:解决并发标记的问题,降低漏标可能性

G1的调优参数

  • -XX:+UseG1GC:启用G1
  • -XX:MaxGCPauseMillis=200:设置期望的最大停顿时间(默认200ms)
  • -XX:G1HeapRegionSize=n:设置Region大小,必须是2的幂,范围1MB-32MB
  • -XX:InitiatingHeapOccupancyPercent=45:设置触发标记周期的堆占用阈值(默认45%)

G1的优势

  1. 可预测的停顿时间,适合大内存、多核CPU环境
  2. 空间整合能力强,降低内存碎片产生
  3. 并发能力强,大部分工作与应用线程并发执行
  4. 适合堆内存较大(6GB+)的系统

ZGC垃圾回收器(Z Garbage Collector)

基本概念

ZGC是JDK 11引入的低延迟垃圾回收器,设计目标是将STW时间控制在10ms以内,即使堆内存达到TB级别。在JDK 15中已成为生产可用状态。

内存布局

ZGC将堆内存划分为大小不等的区域(Z Page),根据大小分为:

  • Small (2MB)
  • Medium (32MB)
  • Large (大于等于32MB)

核心技术

  1. 着色指针(Colored Pointers)

    ZGC在64位指针中保留了一些位作为标记位,用于标记对象状态:

    • Marked0/Marked1位:用于标记活跃对象
    • Remapped位:指示对象是否已经被重定位
    • Finalizable位:标记是否可终结
  2. 读屏障(Load Barrier)

    在应用程序访问对象引用时,ZGC会检查指针的标记位,必要时进行处理,确保应用始终能看到正确的引用

  3. 并发处理: 几乎所有GC操作都在并发阶段完成,包括并发标记、并发重定位、并发引用处理等

ZGC工作流程

  1. 并发标记(Concurrent Mark):标记所有可达对象
  2. 并发准备重定位(Concurrent Prepare for Relocate):选择要清理的区域
  3. 并发重定位(Concurrent Relocate):将存活对象复制到新位置
  4. 并发重映射(Concurrent Remap):更新引用,指向对象的新位置

整个过程中STW阶段极短,主要包括初始标记、最终标记等少量操作,大部分工作与应用线程并发执行。

ZGC的调优参数

  • -XX:+UseZGC:启用ZGC
  • -XX:ZAllocationSpikeTolerance:调整ZGC对内存分配速率突增的容忍度
  • -XX:ConcGCThreads:并发GC线程数
  • -XX:ZCollectionInterval:两次GC之间的最小时间间隔
  • -XX:ZFragmentationLimit:内存碎片百分比阈值

ZGC的优势

  1. 超低延迟:GC停顿时间不超过10ms,且与堆大小无关
  2. 可扩展性强:支持8MB-16TB的堆内存范围
  3. 高吞吐量:即使在TB级别堆上也能保持高吞吐量,只比吞吐量收集器低约15%
  4. 适合延迟敏感型应用:如金融交易、游戏服务器、实时数据处理系统

G1与ZGC的对比

特性G1ZGC
首次发布JDK 7JDK 11
默认GCJDK 9+
停顿时间预测停顿,通常在100-200ms<10ms,与堆大小无关
内存占用相对较小较高(约10-15%)
CPU开销中等较高
碎片处理较好极佳
适用场景6GB-100GB的堆内存,可接受短暂停顿超大堆内存,极低延迟要求
JVM参数-XX:+UseG1GC-XX:+UseZGC

选择建议

  • 如果系统内存<4GB,可以考虑使用ParallelGC
  • 如果系统内存在4GB-8GB,且关注吞吐量,可以使用G1
  • 如果系统对延迟非常敏感,且内存>8GB,建议使用ZGC
  • 企业级应用通常G1是较为均衡的选择,能满足大多数场景需求
  • 金融、游戏等对延迟极为敏感的领域,ZGC是更好的选择

未来发展

随着JDK的更新,ZGC正在不断完善。JDK 16中已经支持了ZGC的类卸载功能,JDK 17中又进一步优化了ZGC的性能。

未来,ZGC很可能会成为Java默认的垃圾收集器,特别是随着大内存系统的普及和低延迟需求的增加。