Skip to content

JEP 278: Additional Tests for Humongous Objects in G1 | 对 G1 中的巨型对象进行附加测试

摘要

为 G1 垃圾收集器的巨大对象功能开发额外的白盒测试。

非目标

我们不会为 G1 急切回收开发测试。

描述

Garbage First (G1) 是一种分代垃圾收集器,它将堆分成相同大小的区域。它具有并发收集阶段,可以与应用程序并行工作,并且是多线程的。

G1 对 大于内存区域一半大小的对象,称为 巨大对象(humongous objects),与其他对象有所不同:

  • 巨大对象总是占据一定数量的区域。 如果巨大对象小于一个区域,则它占据整个区域。如果巨大对象大于 N 个区域且小于 (N+1) 个区域,则它占据 (N+1) 个区域。在最后一个区域中如果有空闲空间则不允许分配。

  • 它们只能在 并发标记周期结束时全面垃圾回收期间 或者在 G1 急切回收情况下的年轻代垃圾回收时 被回收。

  • 它们 永远不能从一个区域移动到另一个区域。

由于 G1 是并发和多线程的 GC,进行黑盒测试非常困难。收集死对象的几种方式,少数并发线程,能够与正在运行的应用程序并行工作以及通常复杂的算法使得几乎不可能了解 G1 的内部状态。为了解决这些问题,我们将扩展 WhiteBox API 并实施使用此 API 检查 G1 内部状态的 Java 测试。我们还将能够在压力测试中重复使用这些新开发的 WhiteBox API 方法。

为了测试处理巨大对象的代码是否按预期工作,我们需要 G1 提供有关堆上巨大对象的内部表示更多细节。我们将向 G1 添加附加的调试方法,这将允许我们从其内部数据结构中获取信息,并控制垃圾收集的启动。后者很重要,因为有三条代码路径可以收集不可达的巨大对象:完整的垃圾回收、并发标记和 G1 急切回收情况下的年轻代垃圾回收。为了测试每条路径,我们需要避免其他路径。

为了帮助解决这个问题,我们将扩展 WhiteBox API,包括:

  • 阻塞和启动并发标记和全面垃圾回收的方法。

  • 枚举 G1 的区域并访问区域属性(例如,空闲 / 占用 / 巨大)的方法。

  • 访问内部 G1 变量的方法,例如自由内存、区域大小和自由区域的数量。

  • 定位堆中的区域的方法,以检查属于巨大对象的区域中是否有分配发生。(这可能是 "堆漫步器" API 的第一步,它允许我们完全遍历 Java 堆)。

备选方案

可能的替代方案包括:

  • 本地内置 JVM 测试。这样的测试可以通过 JVM 标志启动。但它们不太适用,因为测试失败可能导致崩溃。JVM 在测试失败后应该能够继续工作,但该方法不能保证这一点。

  • 本地测试。这将需要向 G1 代码添加调试方法,并实际开发本地 WhiteBox API。这种方法存在某些缺点:我们将无法将这些调试方法用于压力测试。然而,更重要的是,仍然没有本地测试框架。

风险和假设

新测试可能需要对 G1 进行更改。这可能会影响 G1 的性能和稳定性,尽管我们认为这不太可能。如果 G1 受到负面影响,那么我们可以构建没有调试方法的产品二进制文件。