跳到主要内容

JEP 333:ZGC:一个可扩展的低延迟垃圾收集器(实验性)

QWen Max 中英对照 JEP 333 ZGC A Scalable Low-Latency Garbage Collector (Experimental)

概述

Z 垃圾收集器,也被称为 ZGC,是一个可扩展的低延迟垃圾收集器。

目标

  • GC 暂停时间不应超过 10 ms
  • 处理从小到大(几百 MB 到许多 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 屏障代码会更加容易。

  • 我们目前在彩色指针中存储与标记和重定位相关的信息。然而,这种方案的多功能性允许我们存储任何类型的信息(只要能放入指针中),并让加载屏障根据这些信息采取任何操作。我们相信这将为许多未来功能奠定基础。举个例子,在异构内存环境中,这可以用于跟踪堆访问模式,以指导 GC 重定位决策,将很少使用的对象移动到冷存储。

性能

定期的性能测量已使用 SPECjbb® 2015 [1] 完成。从吞吐量和延迟的角度来看,性能表现良好。以下是典型的基准测试分数(以百分比表示,相对于 ZGC 的 max-jOPS 进行标准化),在复合模式下使用 128 GB 堆对 ZGC 和 G1 进行比较。

Benchmark ScoresZGCG1
Throughputxx %xx %
Latencyxx %xx %

注:具体的数值(xx %)需要根据实际的测试结果进行填充。

(越高越好)

ZGC
max-jOPS: 100%
critical-jOPS: 76.1%

G1
max-jOPS: 91.2%
critical-jOPS: 54.7%

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

(数值越低越好)

ZGC
avg: 1.091ms (+/-0.215ms)
95th percentile: 1.380ms
99th percentile: 1.512ms
99.9th percentile: 1.663ms
99.99th percentile: 1.681ms
max: 1.681ms

G1
avg: 156.806ms (+/-71.126ms)
95th percentile: 316.672ms
99th percentile: 428.095ms
99.9th percentile: 543.846ms
99.99th percentile: 543.846ms
max: 543.846ms

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

[1] SPECjbb® 2015 是 Standard Performance Evaluation Corporation(spec.org)的注册商标。实际结果并不被视为合规,因为被测系统 (SUT) 可能不符合 SPEC 对普遍可用性的要求。

限制

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

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

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

构建与调用

按照惯例,JVM 中的实验性功能默认会被构建系统禁用。ZGC 作为一项实验性功能,除非在编译时通过配置选项 --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 特定属性和功能的其他测试。

依赖