美团对 Java 新一代垃圾回收器 ZGC 的探索与实践

。定位到根本原因,每个表达式都是一个类中一个方法 。随着运行时间越长,执行次数增加,这些方法会被JIT优化编译进入到Code Cache中,导致CodeCache越来越大 。
解决方法:JIT有一些参数配置可以调整JIT编译的条件,但对于我们的问题都不太适用 。我们最终通过业务优化解决,删除不需要执行的Aviator表达式,从而避免了大量Aviator方法进入CodeCache中 。
值得一提的是,我们并不是在所有这些问题都解决后才全量部署所有集群 。即使开始有各种各样的毛刺,但计算后发现,有各种问题的ZGC也比之前的CMS对服务可用性影响小 。所以从开始准备使用ZGC到全量部署,大概用了2周的时间 。在之后的3个月时间里,我们边做业务需求,边跟进这些问题,最终逐个解决了上述问题,从而使ZGC在各个集群上达到了一个更好表现 。
升级ZGC效果
延迟降低
| TP(Top Percentile)是一项衡量系统延迟的指标:TP999表示99.9%请求都能被响应的最小耗时;TP99表示99%请求都能被响应的最小耗时 。
在Zeus服务不同集群中,ZGC在低延迟(TP999 < 200ms)场景中收益较大:

  • TP999:下降12~142ms,下降幅度18%~74% 。
  • TP99:下降5~28ms,下降幅度10%~47% 。
超低延迟(TP999 < 20ms)和高延迟(TP999 > 200ms)服务收益不大,原因是这些服务的响应时间瓶颈不是GC,而是外部依赖的性能 。
吞吐下降
对吞吐量优先的场景,ZGC可能并不适合 。例如,Zeus某离线集群原先使用CMS,升级ZGC后,系统吞吐量明显降低 。究其原因有二:第一,ZGC是单代垃圾回收器,而CMS是分代垃圾回收器 。单代垃圾回收器每次处理的对象更多,更耗费CPU资源;第二,ZGC使用读屏障,读屏障操作需耗费额外的计算资源 。
总结
ZGC作为下一代垃圾回收器,性能非常优秀 。ZGC垃圾回收过程几乎全部是并发,实际STW停顿时间极短,不到10ms 。这得益于其采用的着色指针和读屏障技术 。
Zeus在升级JDK 11+ZGC中,通过将风险和问题分类,然后各个击破,最终顺利实现了升级目标,GC停顿也几乎不再影响系统可用性 。
最后推荐大家升级ZGC,Zeus系统因为业务特点,遇到了较多问题,而风控其他团队在升级时都非常顺利 。
参考文献
  • ZGC官网
  • 彭成寒.《新一代垃圾回收器ZGC设计与实现》. 机械工业出版社, 2019.
  • 从实际案例聊聊JAVA应用的GC优化
  • Java Hotspot G1 GC的一些关键技术
附录
如何使用新技术
在生产环境升级JDK 11,使用ZGC,大家最关心的可能不是效果怎么样,而是这个新版本用的人少,网上实践也少,靠不靠谱,稳不稳定 。其次是升级成本会不会很大,万一不成功岂不是白白浪费时间 。所以,在使用新技术前,首先要做的是评估收益、成本和风险 。
评估收益
对于JDK这种世界关注的程序,大版本升级所引入的新技术一般已经在理论上经过验证 。我们要做的事情就是确定当前系统的瓶颈是否是新版本JDK可解决的问题,切忌问题未诊断清楚就采取措施 。评估完收益之后再评估成本和风险,收益过大或者过小,其他两项影响权重就会小很多 。
以本文开头提到的案例为例,假设GC次数不变(10次/分钟),且单次GC时间从40ms降低10ms 。通过计算,一分钟内有100/60000 = 0.17%的时间在进行GC,且期间所有请求仅停顿10ms,GC期间影响的请求数和因GC增加的延迟都有所减少 。
评估成本
这里主要指升级所需要的人力成本 。此项相对比较成熟,根据新技术的使用手册判断改动点 。跟做其他项目区别不大,不再具体细说 。
在我们的实践中,两周时间完成线上部署,达到安全稳定运行的状态 。后续持续迭代3个月,根据业务场景对ZGC进行了更契合的优化适配 。
评估风险
升级JDK的风险可以分为三类:
  • 兼容性风险:Java程序JAR包依赖很多,升级JDK版本后程序是否能运行起来 。例如我们的服务是从JDK 7升级到JDK 11,需要解决较多JAR包不兼容的问题 。
  • 功能风险:运行起来后,是否会有一些组件逻辑变更,影响现有功能的逻辑 。
  • 性能风险:功能如果没有问题,性能是否稳定,能稳定的在线上运行 。
经过分类后,每类风险的应对转化成了常见的测试问题,不再属于未知风险 。风险是指不确定的事情,如果不确定的事情都能转化成可确定的事情,意味着风险已消除 。


推荐阅读