Z Garbage Collect,简称ZGC,早期是Oracle的一个内部项目,在2017年Oracle公司决定将ZGC进行开源。在JDK11(JDK在本文中特指Oracle公司发布的JDK,下同)时,ZGC代码正式合入到主干分支,成为一款标准的官方垃圾回收器。ZGC发布时,由于功能、性能等方面均不完全成熟,所以是一款“实验性质”的垃圾回收器。在经过了JDK的4个迭代开发之后,在JDK15中,ZGC正式升级为一款可用于生产环境的垃圾回收器。
JDK中已经包含了不少的垃圾回收器实现,例如Parallel Scavenge(简称PS), Concurrent-Mark-Sweep(简称CMS), Garbage First(简称G1)等。为什么ZGC能够被引入到JDK中?
ZGC的定位是一款可扩展性、低时延的垃圾回收器,它的主要目标有3个,分别是:
-
支持超大堆内存(TB级别),目前ZGC可以支持堆内存的范围在8MB到16TB之间 最大停顿时间在毫秒级,即不超过10毫秒
-
停顿时间不会随着堆内存的增加而增加
ZGC算法介绍
-
标记:识别堆内存空间中的活跃对象 转移:将内存空间中的活跃对象转移到一块新的内存空间中,对象原来的空间已被回收
重定位:对象的位置发生了变化,对象的引用关系应该更新,确保对象之间的引用都指向对象新的位置
除了并发执行这个特点之外,ZGC在内存管理时采用了分区的管理形式,使得内存的管理更为灵活。同时完善了NUMA-Aware的功能,在NUMA系统中能取得更好的性能。
ZGC发展历程的关键里程
JDK11:在该版本中,ZGC作为实验性中的垃圾回收器引入。在初始发布中,ZGC仅支持Linux平台,且仅仅支持运行在64位系统之上。但在该版本中ZGC的整体框架已经全部实现,包括Colored Pointer,Load Barrier, NUMA-Aware等重要功能。
JDK12:ZGC引入了一个最主要的功能,并发类卸载。从而大大降低了停顿时间。
JDK13:引入了aarch64的支持,此时ZGC可以运行在x64和ARM平台的Linux之上。另外ZGC为了迎合现代云场景的诉求,加入了内存释放(归还给操作系统)的功能。
JDK14:该版本最终的功能就是支持在x64平台的Windows和MacOS系统,从而使得多个平台可以使用ZGC。
JDK15:该版本中ZGC成为一个生产可用的垃圾回收器。在该版本中增加了通用功能的支持,例如支持压缩指针(在小内存下性能更优),支持Class Data Sharing,支持堆空间使用NVRAM等。使得ZGC功能完备。
华为毕昇JDK对ZGC的支持
另外Oracle公司对于JDK的使用策略也发生了变化,对于商业环境中使用JDK8U202之后的版本都需要购买Oracle的License(Oracle的云产品或者Oracle授权产品除外),目前License的价格大概是25美金/processor。如果在商业环境中使用JDK8U202之后的版本,但并未付费,是违反Oracle公司对于JDK的商业协议,可能会收到来自Oracle公司的诉求。
JDK的开发是以OpenJDK项目为基础,而OpenJDK项目是完全开源的(许可证是 GPLv2+CE,也就是说使用者可以根据OpenJDK的源码开发自己的JDK,并开源自研的JDK)。
基于上述原因以及其他外部原因,华为公司也基于OpenJDK开发了自己的产品:毕昇JDK,目前毕昇JDK已经在码云上开源。毕昇JDK致力于维护安全、稳定、高效的JDK。目前对于LTS的JDK8和JDK11都进行投入了大量的人力和物力。毕昇JDK全部通过JCK、FUZZ,JTreg等测试套。毕昇JDK运行在华为内部500多个产品上,积累了大量使用场景和Java开发者反馈的问题和诉求,解决了业务实际运行中遇到的多个问题,并在ARM架构上进行了性能优化,毕昇JDK运行在大数据等场景下可以获得更好的性能。
这里以ZGC为例,稍微介绍几个毕昇JDK11相对于JDK11做了哪些工作。
支持aarch64。在JDK11中ZGC仅支持x64平台,毕昇JDK11是目前所有开源JDK中唯一支持x64和aarch64的产品。
-
Bug修复。在整个运行和维护过程中,修复了一些bug,这些bug有些是社区在JDK14或者JDK15进行修复,有些我们是将社区的修复方案回合到毕昇JDK11,有些是我们在毕昇JDK11重新做了实现。举一个简单的例子,在我们的测试中发现,对于某些测试用例对于G1 GC和ZGC运行结果不一致,经过案例分析,对汇编代码进行跟踪,最终发现在ZGC的C1支持中对于浮点数寄存器使用有误,据此进行了修复。在毕昇JDK11上ZGC稳定性远远超过JDK11,可以在生产环境中使用。
ZGC的未来发展点
ZGC非常完美的实现了低时延的需求,把尽可能的工作并发执行,在并发执行的时候,通常需要Mutator“帮助”Collector,完成本应该由Collector完成的工作,也就说Mutator除了完成应用代码的执行还需要做一些额外的辅助工作,由于Mutator不能专心执行应用代码,这会造成ZGC在吞吐率方面的不足。另外目前ZGC是单代回收,更加放大了ZGC的吞吐率不足的缺点。要解决吞吐率的问题,通常的是方法是实现分代回收。另外一种方法是线程局部回收(即Thread Local Garbage Collection,简称为TLGC),TLGC来说天然对于多核的系统更为友善。TLGC的核心思想是:每个Mutator在发现内存不足时,优先回收Mutator自己分配的内存。TLGC的另一个好处是不需要全局的暂停,故不会增加停顿时间。在实际应用中,Mutator分配的对象可能被另外一个Mutator使用,这就所谓的逃逸对象,对于逃逸对象在TLGC中不能回收。这里逃逸分析还会涉及到真逃逸、假逃逸,所谓假逃逸指的是对象可以被另外一个Mutator看到,但是在实际运行过程中并未访问。所以TLGC的核心是一套高效的逃逸分析技术,包含准确的识别假逃逸。
目前华为毕昇JDK团队正在设计和实现ZGC的分代和TLGC。期望不久的将来大家可以在毕昇JDK中看到这些特性。
参考文献
-
ZGC官网信息:https://wiki.openjdk.java.net/display/zgc/
毕昇JDK11码云地址:https://gitee.com/openeuler/bishengjdk-11
图书《新一代垃圾回收器ZGC设计与实现》——彭成寒
本文分享自微信公众号 - openEuler(openEulercommunity)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。