JEP 304:垃圾收集器接口
概述
通过引入一个简洁的垃圾收集器(GC)接口,改善不同垃圾收集器的源代码隔离性。
目标
- 提高 HotSpot 内部 GC 代码的模块化程度
- 在不干扰当前代码库的情况下,简化向 HotSpot 添加新 GC 的过程
- 更轻松地从 JDK 构建中排除某个 GC
非目标
- 实际添加或移除一个 GC 并不是目标。
- 这项工作将朝着 HotSpot 中 GC 算法的构建时隔离取得进展,但完全实现构建时隔离并不是目标(这是另一个 JEP 的任务)。
成功指标
- 如果 GC 实现大部分包含在其各自的
src/hotspot/share/gc/$NAME
目录以及可能的src/hotspot/cpu/share/gc/$NAME
目录中的源文件内,那么该实现将被视为成功。这些目录之外的极少代码应包含来自这些目录的文件,并且应该有非常少的 GC 特定的if
-else
分支。 - 由于此重构,性能不应出现倒退。
动机
每个垃圾收集器的实现目前由其 src/hotspot/share/gc/$NAME
目录中的源文件组成,例如 G1 位于 src/hotspot/share/gc/g1
,CMS 位于 src/hotspot/share/gc/cms
等。然而,HotSpot 源代码的各个部分散布着一些零散的代码。例如,大多数垃圾收集器需要特定的屏障(barriers),这些屏障需要在运行时、解释器、C1 和 C2 中实现。这些屏障并不包含在垃圾收集器的特定目录中,而是实现在共享的解释器、C1 和 C2 源代码中(通常由长长的 if
-else
链保护)。同样的问题也适用于诊断代码,例如 MemoryMXBeans
。这种源代码布局存在以下几个缺点:
- 对于 GC 开发者来说,实现一个新的垃圾收集器需要了解所有这些不同的地方,以及如何根据其特定需求进行扩展。
- 对于非 GC 开发者的 HotSpot 开发者来说,很难找到特定 GC 的某段代码的位置。
- 在构建时排除特定的垃圾收集器变得很困难。长期以来,
#define
INCLUDE_ALL_GCS
一直是一种仅内置串行收集器来构建 JVM 的方法,但这种机制正变得过于不灵活。
一个更简洁的 GC 接口将使实现新的收集器变得更加容易,它会使代码更加清晰,并且在构建时更容易排除一个或多个收集器。添加一个新的垃圾收集器应该只是实现一套有良好文档记录的接口的问题,而不是找出 HotSpot 中所有需要更改的地方。
描述
GC 接口将由现有的 CollectedHeap
类定义,每个垃圾收集器都需要实现该类。CollectedHeap
类将驱动垃圾收集器与 HotSpot 其余部分之间交互的大多数方面(在实例化 CollectedHeap
之前需要一些实用工具类)。更具体地说,垃圾收集器的实现必须提供以下内容:
- 堆(heap),是
CollectedHeap
的子类 - 屏障集合(barrier set),是
BarrierSet
的子类,它为运行时实现各种屏障 CollectorPolicy
的一个实现GCInterpreterSupport
的一个实现,它为解释器的垃圾回收实现各种屏障(使用汇编指令)GCC1Support
的一个实现,它为 C1 编译器的垃圾回收实现各种屏障GCC2Support
的一个实现,它为 C2 编译器的垃圾回收实现各种屏障- 初始化最终的垃圾回收特定参数
- 设置
MemoryService
、相关的内存池、内存管理器等
在多个垃圾收集器之间共享的实现细节的代码应该存在于一个辅助类中。这样,不同的垃圾收集器实现可以轻松使用它。例如,可以有一个辅助类,实现各种用于卡表支持的屏障,任何需要卡表后屏障的垃圾收集器都可以调用该辅助类的相应方法。通过这种方式,该接口提供了实现全新屏障的灵活性,同时允许以混合搭配的方式重用现有代码。
替代方案
另一种方法是继续使用当前的架构。这样做的话,虽然很可能还可以正常运行一段时间,但会阻碍新的 GC 算法的开发以及旧算法的移除。
测试
这完全是重构。之前所有能正常运行的功能在重构后也必须能够正常运行,并且性能不应出现退化。运行标准的回归测试套件应该就足够了,不需要开发新的测试。
风险与假设
风险较低,这主要是对 HotSpot 内部代码的重构。存在性能可能受到损害的风险,例如,如果引入了额外的虚拟调用。这种风险可以通过持续的性能测试来减轻。
依赖
该 JEP 将有助于 JEP 291:弃用并发标记清除(CMS)垃圾收集器,因为它提供了一种隔离 CMS 的方法,并在需要时允许由其他人来维护它。
该 JEP 还将有助于 JEP 189:Shenandoah:一种超低暂停时间的垃圾收集器,并使其更改不那么具有侵入性。