
前言
JDK 21 作为最新的 LTS 版本,除了虚拟线程等特性外,最值得关注的是分代 ZGC 的正式落地。JDK 21 和后续版本提供了一系列值得注意的功能,例如虚拟线程、switch 的模式匹配和分代 ZGC。
ZGC 是 Java 中高度可扩展、低延迟的垃圾收集器,在 JDK 21 中通过 JEP 439 进行了更新,成为分代垃圾收集器。那么,如何使用分代 ZGC?切换到分代 ZGC 后会获得什么样的性能?让我们来看看。
什么是 ZGC
ZGC 最初作为实验性功能随 JDK 11 发布,在 JDK 15 中,它升级为生产功能。ZGC 的设计具有高度可扩展性,支持高达 16TB 的堆大小,同时保持亚毫秒级的暂停时间。
ZGC 能够通过几乎完全并发的方式实现这些目标。这意味着 ZGC 在应用程序运行时执行其工作,分配新对象、扫描无法访问的对象、压缩堆等。
但是,这种设计选择的代价是,应用程序的吞吐量降低了,因为应用程序可以使用的 CPU 资源却被 ZGC 利用了。

什么是分代 ZGC
分代 ZGC 旨在通过扩展 ZGC 来为年轻对象和老对象维护单独的代,从而提高应用程序性能。年轻对象往往在年轻时就死亡;维护单独的代将允许 ZGC 更频繁地收集年轻对象。使用分代 ZGC 运行的应用程序应获得以下好处:分配停滞的风险更低、所需的堆内存开销更低、垃圾收集 CPU 开销更低。与非分代 ZGC 相比,这些好处应该可以在不显著降低吞吐量的情况下实现。

分代垃圾收集器在逻辑上将堆分为两代:年轻代和老一代。如上图所示,分配对象时,它最初被放入年轻代,并经常被扫描。如果对象存活时间足够长,它将被提升到老一代。
分代垃圾收集器执行此行为是为了利用弱代假设,该假设假定大多数对象在创建后不久就变得无法访问。
因此,ZGC 通过频繁扫描年轻一代,可以更有效地利用 CPU 资源。
在开发 Generational ZGC 的过程中,ZGC 工程团队定期进行内部性能测试,以确保 Generational ZGC 达到目标。
就吞吐量而言,分代 ZGC 比 JDK 17 中的单代 ZGC 提高了约 10%,比 JDK 21 中的单代 ZGC 提高了 10% 多一点,而 JDK 21 中出现了小幅倒退。

下面这张图,虽然看起来与上一张几乎相同,但它讲述的故事却有所不同。这张图显示,与单代 ZGC 相比,分代 ZGC 的平均延迟略有下降。

然而,当我们查看实际数字时,我们发现差异仅有 2 到 3 微秒。








