[译]ZGC: 一个可伸缩的低延迟垃圾收集器
翻译自:JEP 333
地址:https://openjdk.java.net/jeps/333
JEP 333在JDK 11中交付,描述的是最初的实验性ZGC。单代、平台限制及解锁实验选项等说法均属于当时版本;ZGC在JDK 15转为正式功能,后续也引入了分代实现。
一、摘要
Z垃圾收集器,也称为ZGC,是一个可伸缩的低延迟垃圾收集器。
二、目标
- GC暂停时间不超过10ms
- 能处理大小从相对较小(几百MB)到非常大(TB级)的堆
- 与使用G1相比,应用程序吞吐量减少不超过15%
- 利用彩色指针和读屏障,为后续GC功能和优化打下基础。
- 支持平台:Linux/x64
load barrier在这里指GC读屏障,即加载对象引用时执行的检查或修正逻辑,不是Java内存模型中用于约束读写顺序的内存屏障。
我们有强烈的雄心在大量相关负载上达成这些目标。但同时也要承认,我们并不把这些目标视为任何可想象的负载都必须满足的硬性要求。
非目标(Non-Goals):本 JEP 的目标不包括为 Linux/x64 以外的平台提供可用实现;如果需求足够,后续可以增加对其他平台的支持。
三、动机
垃圾收集是Java的主要优势之一。但是,当垃圾收集暂停太长时,就会对应用程序的响应时间产生负面影响。通过大幅度缩短停顿时间,我们可以让Java适用于更多类型的应用程序。
此外,现代系统中可用的内存数量还在继续增长。用户和应用程序开发人员希望JVM能够以一种有效的方式充分利用这种内存,并且不会出现很长的GC暂停时间。
四、 描述
ZGC是一个并发的、单代(不再区分新生代和老年代)的、基于region的、支持numa的压缩收集器。Stop-the-world阶段仅限于根扫描,所以GC暂停时间不会随着堆或存活对象的多少而增加。
ZGC的核心设计原则之一是结合读屏障和彩色对象指针,让对象重定位等操作与Java应用线程并发执行。Java线程加载对象引用时会经过读屏障。彩色指针除对象地址外,还携带元数据,供读屏障判断线程使用该引用前是否需要采取操作。例如,对象已经被重定位时,读屏障会检测并修正引用。
与其他技术相比,我们认为颜色指针方案提供了一些非常吸引人的特性。特别是:
在对象重定位/压缩阶段,即使指向待回收区域的指针还没有全部修正,也可以回收并重用该区域的内存。这有助于降低堆开销,也不必另行实现一套标记压缩算法来处理Full GC。
这允许我们使用相对较少且简单的GC屏障。这有助于降低运行时开销。这还意味着在解释器和JIT编译器中更容易实现、优化和维护GC barrier代码。
我们目前将标记和重定位信息存储在彩色指针中,但它也可以携带其他信息(只要指针中有空间),让读屏障据此采取所需的操作。这为后续特性留下了空间。例如,在异构内存环境中,可以跟踪堆访问模式,指导GC把很少使用的对象移到冷存储(不常访问的内存区域)。
五、性能
我们已经使用SPECjbb 2015[1]做了常规性能测试。从吞吐量和延迟角度来看,性能都很好。下面是使用128G堆在复合模式下比较ZGC和G1的典型基准分数(以百分比为单位,根据ZGC的max-jOPS进行标准化)【原文:Below are typical benchmark scores (in percent, normalized against ZGC’s max-jOPS), comparing ZGC and G1, in composite mode using a 128G heap.】:
越高越好
1 | ZGC |
下面是来自相同基准测试的GC暂停时间。ZGC设法保持远低于10ms的目标。注意,确切的数字可能会根据使用的机器和设置而变化(上下都有,但不是很明显)。
越低越好
1 | ZGC |
我们还对其他各种SPEC®基准测试和内部工作负载进行了特别的性能测量。一般情况下,ZGC能够维护个位数的毫秒暂停时间。
六、 局限性
ZGC的初始实验版本将不支持类卸载。默认情况下,ClassUnloading 和 ClassUnloadingWithConcurrentMark 选项将被禁用。即便你启用也是不生效的。
此外,ZGC最初不支持JVMCI(即Graal)。如果启用EnableJVMCI选项,将打印一条错误消息。
这些限制将在本项目的后期解决。
七、 构建和使用
按照惯例,构建系统默认禁用JVM中的实验性特性。ZGC是一个实验性特性,因此不会出现在JDK构建中,除非在编译时使用configure选项:
1 | --with-jvm-features=zgc |
显式地启用它。
(ZGC将出现在Oracle发布的所有Linux/x64 JDK版本中)
JVM中的实验特性还需要在运行时显式地解锁。因此,要启用/使用ZGC,需要以下JVM选项:
1 | -XX:+UnlockExperimentalVMOptions -XX:+UseZGC |
有关如何设置和调优ZGC的更多信息,请参阅ZGC项目Wiki(wiki地址:https://wiki.openjdk.java.net/display/zgc/Main)。
备选方案(Alternatives)
- 一个显而易见的备选方案是为 G1 增加并发压缩能力。该方案曾被大量原型验证,但最终被放弃。我们发现,把这一功能硬塞进一个从设计上就不是为此目的服务的代码库,同时还要保持 G1 的稳定性等良好特性,是不可行的。
- 一个理论上的备选方案是设法改进 CMS。但以 CMS 算法为基础构建低延迟收集器,既不可取也不可行,原因包括:不支持压缩、remark 阶段无界、代码库复杂,以及 CMS 已被弃用(JEP 291)。
- Shenandoah 项目正在探索使用 Brooks 指针来实现并发操作(JEP 189)。
测试(Testing)
我们现有的大多数功能和压力测试都与收集器无关,可以直接复用;此外还将增加针对 ZGC 特有属性和功能的测试。
依赖(Dependencies)
- JEP 304:垃圾收集器接口(Garbage Collector Interface)
- JEP 312:线程本地握手(Thread-Local Handshakes)
ZGC paper可以参考Azul Pauseless GC Algorithm:
ZGC 简介PPT:
https://github.com/jiankunking/books-recommendation/blob/master/GC/ZGC/ZGC-FOSDEM-2018.pdf