Skip to content

JEP 333: ZGC: A Scalable Low-Latency Garbage Collector (Experimental) | ZGC:可扩展的低延迟垃圾收集器(实验性)

摘要

Z Garbage Collector(简称 ZGC)是一个可伸缩的低延迟垃圾收集器。

目标

  • GC 暂停时间不应超过 10 毫秒
  • 处理从相对较小(几百兆字节)到非常大(许多 TB)的堆大小
  • 与使用 G1 相比,应用程序吞吐量下降不超过 15%
  • 为未来利用彩色指针和加载屏障的 GC 特性和优化奠定基础
  • 最初支持的平台:Linux/x64

我们有着强烈的愿望,希望对于大量相关的工作负载能够达到这些目标。同时,我们也认识到,我们并不把这些目标视为每个可能的工作负载的硬性要求。

非目标

除了 Linux/x64 平台之外,提供其他平台的工作实现并不是目标。如果有足够的需求,可以稍后添加对其他平台的支持。

动机

垃圾收集是 Java 的主要优势之一。然而,当垃圾收集暂停变得过长时,它们开始负面地影响应用程序的响应时间。通过消除或大幅度减少 GC 暂停的长度,我们将使 Java 成为更广泛应用程序的更具吸引力的平台。

此外,现代系统中可用的内存量持续增长。用户和应用程序开发人员期望 JVM 能够以高效的方式充分利用这些内存,并且没有长时间的 GC 暂停。

描述

简而言之,ZGC 是一个并发的、单代的、基于区域的、NUMA 感知的、压缩式的垃圾收集器。Stop-the-World(暂停所有应用程序线程)的阶段仅限于根扫描,因此 GC 暂停时间不会随着堆或活动集的大小而增加。

ZGC 的一个核心设计原则/选择是使用加载屏障与彩色对象指针(即彩色 oops)相结合。这使得 ZGC 能够在 Java 应用程序线程运行时执行并发操作,如对象重新定位。从 Java 线程的角度来看,加载 Java 对象中引用字段的行为会受到加载屏障的约束。除了对象地址之外,彩色对象指针还包含加载屏障用于确定在允许 Java 线程使用指针之前是否需要采取某些操作的信息。例如,对象可能已经被重新定位,在这种情况下,加载屏障将检测到这种情况并采取适当的操作。

与替代技术相比,我们认为彩色指针方案提供了一些非常有吸引力的特性。特别是:

  • 它允许我们在重新定位/压缩阶段期间,在指向已回收/重新使用的区域的指针被修复之前,回收和重用内存。这有助于降低总体堆开销。这也意味着我们不需要实现一个单独的标记 - 压缩算法来处理完整的 GC。

  • 它允许我们拥有相对较少且简单的 GC 屏障。这有助于降低运行时开销。这也意味着在我们的解释器和 JIT 编译器中实现、优化和维护 GC 屏障代码变得更加容易。

  • 目前,我们在彩色指针中存储与标记和重新定位相关的信息。但是,该方案的多样性允许我们存储任何类型的信息(只要我们能将其放入指针中),并让加载屏障基于该信息执行任何想要的操作。我们相信这将为未来扩展 ZGC 提供坚实的基础,包括支持像自动扩展的类定义(例如,通过类重新定义)这样的特性。

性能

已经使用 SPECjbb® 2015 [1] 进行了常规的性能测量。从吞吐量和延迟的角度来看,性能表现良好。以下是典型的基准测试得分(以百分比表示,以 ZGC 的最大 jOPS 为基准),比较了使用 128G 堆的复合模式下 ZGC 和 G1 的表现。

(分数越高越好)

txt
ZGC
       最大 jOPS: 100%
   关键 jOPS: 76.1%

G1
       最大 jOPS: 91.2%
   关键 jOPS: 54.7%

以下是来自相同基准测试的典型 GC 暂停时间。ZGC 成功地将暂停时间保持在 10ms 目标以下。请注意,确切的数字可能会有所不同(上下浮动,但不会显著变化),这取决于使用的具体机器和设置。

(数值越低越好)

txt
ZGC
                平均值: 1.091ms (+/-0.215ms)
    95% 的百分位数: 1.380ms
    99% 的百分位数: 1.512ms
  99.9% 的百分位数: 1.663ms
 99.99% 的百分位数: 1.681ms
                最大值: 1.681ms

G1
                平均值: 156.806ms (+/-71.126ms)
    95% 的百分位数: 316.672ms
    99% 的百分位数: 428.095ms
  99.9% 的百分位数: 543.846ms
 99.99% 的百分位数: 543.846ms
                最大值: 543.846ms

还在各种其他 SPEC®基准测试和内部工作负载上进行了临时的性能测量。通常,ZGC 能够保持个位数的毫秒暂停时间。

[1] SPECjbb® 2015 是 Standard Performance Evaluation Corporation(spec.org)的注册商标。由于被测系统(SUT)可能不符合 SPEC 的通用可用性要求,因此实际结果不代表合规性。

限制

ZGC 的初始实验版本将不支持类卸载。ClassUnloadingClassUnloadingWithConcurrentMark选项将默认被禁用。启用它们将不会产生任何效果。

此外,ZGC 最初将不支持 JVMCI(即 Graal)。如果启用了EnableJVMCI选项,则会打印一条错误消息。

这些限制将在该项目的后期阶段得到解决。

构建和调用

按照惯例,JVM 中的实验特性在默认情况下会被构建系统禁用。ZGC 作为一个实验特性,因此除非在编译时使用 configure 选项--with-jvm-features=zgc明确启用,否则它不会出现在 JDK 构建中。

(ZGC 将出现在 Oracle 生产的所有 Linux/x64 JDK 构建中)

JVM 中的实验特性也需要在运行时明确解锁。要启用/使用 ZGC,因此需要以下 JVM 选项:-XX:+UnlockExperimentalVMOptions -XX:+UseZGC

有关如何设置和调整 ZGC 的更多信息,请参阅ZGC 项目 Wiki

备选方案

  • 一个显而易见的备选方案是向 G1 添加并发压缩功能。这一备选方案曾经过广泛的原型设计,但最终被放弃。我们发现在一个从未为此目的设计的代码库中强行添加这一功能,同时保持 G1 的稳定性和其他良好特性,是不可行的。

  • 一个理论上的备选方案是以某种方式改进 CMS。然而,基于 CMS 算法构建低延迟收集器既不是一个吸引人也不是一个可行的选择,原因有很多。这些原因包括不支持压缩、无限制的重标记阶段、复杂的代码库,以及它已经被弃用(JEP 291)。

  • Shenandoah 项目正在探索使用 Brooks 指针来实现并发操作(JEP 189)。

测试

我们现有的大部分功能和压力测试都是与收集器无关的,可以原样复用。同时,我们将添加针对 ZGC 特定属性和功能的额外测试。

依赖项