Java GC的STW(Stop-The-World)停顿,一直是低延迟业务的核心痛点。传统CMS、G1等回收器,虽然通过并发优化大幅缩短了停顿,但依然无法摆脱「停顿时间随堆内存、存活对象数量增长而劣化」的魔咒,常规场景下仍有十几到几十毫秒的停顿。而JDK11开源、JDK15正式生产就绪、JDK21成为默认回收器的ZGC(Z Garbage Collector),直接将STW停顿稳定控制在亚毫秒级,哪怕是TB级堆内存,停顿时间也不会随堆大小增长,彻底重构了Java GC的底层设计逻辑,是现代Java低延迟服务的核心利器。
一、ZGC的核心设计目标与突破
ZGC从诞生之初,就定下了三个不可妥协的核心目标:
- 停顿时间不超过10ms,且永远不会随堆内存、存活对象数量的增长而变长;
- 支持8MB ~ 16TB的超宽堆内存范围,适配从微服务到大数据处理的全场景;
- 吞吐量损耗不超过15%,在低延迟的前提下,保证业务的处理能力。
它和传统GC的本质区别,是彻底解决了「对象压缩转移必须STW」的行业难题——传统GC在整理堆内存、移动存活对象时,必须暂停所有用户线程,否则会出现对象引用错乱、地址失效的问题。而ZGC通过两大核心技术,把几乎所有GC操作都放到了并发阶段执行,仅保留了极短的根节点枚举STW阶段。
二、ZGC的两大核心底层技术
1. 有色指针(Colored Pointers):把GC状态存在指针里
这是ZGC的核心基石,它利用了64位系统虚拟地址空间的冗余位,打破了传统GC的设计范式。
64位系统的虚拟地址空间远大于实际使用的内存,ZGC截取了64位对象指针的高4位未使用位,存储4个GC状态标记位:Marked0、Marked1、Remapped、Finalizable。这4个标记位,直接记录了对象的存活状态、地址转移状态,而不是像传统GC那样,把标记信息存在对象头的Mark Word中。
这个设计带来了革命性的优势:
- 对象的GC状态和引用地址绑定,无需修改对象头,彻底避免了并发标记时的多线程竞态问题;
- 无需遍历整个堆内存就能获取对象的GC状态,大幅降低了GC的遍历开销;
- 配合读屏障,可在用户线程运行的同时,并发完成对象的转移与引用修正。
2. 读屏障(Load Barrier):并发安全的核心保障
读屏障是JVM注入到字节码中的一小段极简逻辑,当用户线程从堆中读取对象引用时,会先执行这段逻辑,检查指针的有色标记位:如果对象已经被GC转移到了新的内存地址,读屏障会自动修正该引用的地址(更新Remapped标记),再返回给用户线程使用。
和传统GC普遍使用的写屏障(修改对象时触发)相比,读屏障的核心优势是:
- 支持并发对象转移:哪怕对象正在被用户线程访问,GC也能同时移动该对象,用户线程读取时会通过读屏障自动修正引用,完全不需要STW暂停;
- 无额外的内存占用:不需要像G1那样维护记忆集(RSet),大幅降低了GC的内存开销;
- 逻辑极简,经过CPU指令级优化,常规场景下的性能损耗极低。
三、ZGC的完整GC周期:STW仅占不到1%的时间
ZGC的GC周期全程以并发执行为主,仅在两个极短的阶段有STW停顿,完整流程分为7步:
- 暂停标记开始(STW):仅枚举GC Roots(线程栈、静态变量、JNI引用等),标记根节点直接引用的对象。停顿时间仅和GC Roots的数量有关,和堆大小完全无关,常规场景下仅0.1~0.5ms。
- 并发标记:和用户线程完全并行,遍历对象图标记所有存活对象,利用有色指针的标记位完成状态更新,全程无STW。
- 暂停标记结束(STW):处理并发标记的边缘场景,清理软引用、弱引用等,停顿时间通常不到1ms。
- 并发准备转移:统计待清理的内存区域,规划存活对象的转移方案,全程并行。
- 暂停转移开始(STW):再次枚举GC Roots,转移根节点直接引用的对象并修正指针,停顿时间依然仅和GC Roots数量相关,亚毫秒级。
- 并发转移:和用户线程并行,将存活对象从待清理的内存区域转移到新区域,同时释放原区域的内存。用户线程访问已转移对象时,读屏障会自动修正引用,全程无STW。
- 并发重映射:修正堆中剩余的未更新引用,该阶段会和下一次GC的并发标记阶段合并,进一步降低不必要的开销。
四、核心认知误区与最佳实践
常见认知误区
- 误区1:ZGC完全没有STW停顿。真相:ZGC依然有STW,但仅在根节点枚举阶段,停顿时间和堆大小、存活对象数无关,常规场景下稳定在1ms以内,远低于G1、CMS。
- 误区2:ZGC的读屏障有巨大性能开销。真相:ZGC的读屏障经过极致优化,仅在读取堆对象引用时触发,常规业务场景下吞吐量损耗仅5%~15%,换来的是消除99%以上的GC停顿毛刺,对低延迟服务完全值得。
- 误区3:ZGC只适合超大堆内存。真相:哪怕是几GB的小堆,ZGC的停顿表现也远优于G1,JDK21已将其作为Linux/x64平台的默认GC,适配绝大多数业务场景。
- 误区4:ZGC和G1一样是分代回收。真相:早期ZGC为非分代设计,JDK21正式引入分代ZGC,针对年轻代短生命周期对象做了专项优化,进一步降低了内存占用和吞吐量损耗,性能再上一个台阶。
生产环境最佳实践
- 低延迟服务优先升级JDK21+,开启分代ZGC,仅需
-XX:+UseZGC -Xmx<堆大小> -Xms<堆大小>三个核心参数,几乎无需复杂调优,即可获得亚毫秒级的停顿表现。 - 避免频繁创建长生命周期的大对象,减少GC Roots的数量,可进一步压缩STW停顿时间。
- 通过
-Xlog:gc*开启GC日志,精准监控GC周期、停顿时间、吞吐量情况,按需调整堆内存大小,避免频繁GC。 - 对于交易系统、API网关、实时数据处理等延迟敏感型服务,ZGC是最优选择,可彻底解决GC停顿导致的接口超时、业务毛刺问题。
结语
ZGC是Java GC发展史上里程碑式的革新,它用有色指针+读屏障的革命性设计,彻底打破了传统GC「停顿时间随堆大小增长」的魔咒,让Java在低延迟、高实时性的场景中,拥有了和原生语言竞争的能力。理解ZGC的底层原理,不仅能彻底搞懂低延迟GC的核心逻辑,更是Java性能调优、低延迟服务架构设计的核心前提。