一、JDK 21垃圾收集器的核心升级
相比JDK 17及之前版本,JDK 21的GC主要有三大变化:
1.ZGC正式支持分代:这是ZGC自JDK 11引入后的最大功能升级,解决了不分代时短期对象回收效率低的问题;
2.G1优化混合回收:在大堆场景下,G1的混合回收停顿更稳定,减少了极端情况下的长停顿;
3.统一GC日志格式 :所有收集器的日志输出格式更一致,比如-Xlog:gc*参数的日志内容标准化,方便监控工具解析。
其中,ZGC分代功能是最值得关注的亮点,官方测试显示,开启分代后ZGC的平均GC停顿时间比不分代版本降低约40%,尤其适合高并发的Java服务。
二、为什么需要ZGC分代?
传统ZGC(JDK 11-19)是不分代的,所有对象都在同一个堆区域中回收。但根据"弱分代假说",90%的对象都是"朝生夕死"的短期对象,只有少数对象会长期存活。如果能把短期对象和长期对象分开管理:
•对短期对象:在小空间内快速回收,减少GC扫描范围;•对长期对象:降低回收频率,避免频繁扫描大对象。
ZGC分代功能正是基于这个原理设计,让GC资源更集中在短期对象上,从而整体降低停顿时间。
三、ZGC分代功能如何配置?
1. 基础启用参数
JDK 21中,ZGC分代功能默认不开启,需要通过JVM参数手动启用:
-XX:+UseZGC -XX:+ZGenerational
•-XX:+UseZGC:指定使用ZGC收集器;•-XX:+ZGenerational:开启分代功能。
2. 分代相关调优参数
新生代大小控制:
•-XX:ZNewSizePercent:新生代最小占比,默认1%;•-XX:ZMaxNewSizePercent:新生代最大占比,默认33%。
生产环境可根据对象生命周期调整,比如短期对象多的系统,可设-XX:ZMaxNewSizePercent=50让新生代更大,加速短期对象回收。
•
晋升年龄阈值 :
对象在新生代经历几次GC后进入老年代,通过-XX:MaxTenuringThreshold设置,默认6次。如果发现很多短期对象提前晋升,可适当调大这个值。
•
其他实用参数 :
-XX:+ZStatistics:打印ZGC详细统计信息,包括新生代回收次数、晋升速率等,方便问题排查;
-XX:ZCollectionInterval=30:设置GC触发间隔,默认动态调整,可根据业务低峰期手动设置。
四、分代ZGC的底层原理
1. 堆内存划分
分代ZGC将堆分为新生代 和老年代,两者物理隔离:
•新生代 :专门存放短期对象,空间小、回收频率高,采用"标记-复制"算法,复制过程由后台线程并发执行,几乎无停顿;•老年代:存放长期存活的对象,采用ZGC原有的"并发标记-整理"算法,通过着色指针和读屏障实现并发操作,停顿时间控制在1毫秒内。
2. 对象晋升流程
1.新对象优先分配在新生代的Eden区;2.新生代GC时,存活对象被复制到Survivor区,年龄+1;3.当对象年龄达到MaxTenuringThreshold,或Survivor区空间不足时,对象晋升到老年代;4.老年代定期进行并发GC,回收不再使用的长期对象。
3. 低延迟的关键技术
•新生代GC完全并发 :复制对象的过程由GC线程后台执行,用户线程可同时运行,无STW停顿;•老年代复用ZGC核心技术:通过着色指针标记对象状态,读屏障处理并发访问冲突,避免传统分代GC的"Stop-The-World"阶段。
五、生产环境实践注意事项
1. JDK版本选择
必须使用JDK 21及以上版本,建议选择LTS版本,稳定性更好。如果是从JDK 17升级,需注意GC日志格式的变化,避免监控工具解析异常。
2. 内存配置建议
•堆内存大小:根据服务器内存设置,建议-Xms和-Xmx设为相同值,避免动态扩容导致的性能波动;•新生代占比:短期对象多的系统,可设-XX:ZMaxNewSizePercent=40-50;长期对象多的系统,可设为20-30%。
3. 监控与问题排查
•监控工具 :使用JDK自带的jstat -gcutil <pid>查看GC统计,或用Grafana+Prometheus+Micrometer监控ZGC分代指标;常见问题:
•新生代过小导致频繁晋升:增大ZMaxNewSizePercent;•老年代内存泄漏:通过jmap -histo:live <pid>分析大对象,结合业务代码优化对象生命周期。
六、总结
JDK 21的ZGC分代功能,结合G1的混合回收优化和统一GC日志,让Java应用的GC性能再上一个台阶。对于追求低延迟的高并发服务,开启ZGC分代是性价比极高的优化手段------只需简单配置-XX:+ZGenerational,再根据业务调整新生代占比,就能显著降低GC停顿时间。