Skip to content

JEP 345: NUMA-Aware Memory Allocation for G1 | G1 的 NUMA 感知内存分配

摘要

通过实现 NUMA 感知的内存分配来提高 G1 在大型机器上的性能。

非目标

  • 并非旨在在 G1 之外的其他收集器上实现 NUMA 支持。
  • 并非旨在支持 Linux 以外的其他操作系统。
  • 并非旨在使 G1 的其他部分(如任务队列窃取、记忆集或细化)也感知 NUMA。

动机

现代多插槽机器越来越多地具有非均匀内存访问(NUMA)特性,即内存不是等距离地从每个插槽或核心访问的。插槽之间的内存访问具有不同的性能特性,访问更远的插槽通常具有更高的延迟。

多年来,通过 -XX:+UseParallelGC 启用的并行收集器已经感知 NUMA。这有助于改善跨多个插槽运行单个 JVM 的配置的性能。其他 HotSpot 收集器并未受益于这一特性,这意味着它们无法利用这种垂直多插槽 NUMA 扩展。特别是大型企业应用程序倾向于在多个插槽上使用大堆配置运行,但它们希望获得在单个 JVM 内运行的管理优势。使用 G1 收集器的用户越来越多地遇到这种扩展瓶颈。

描述

G1 的堆内存被组织成一系列固定大小的区域。一个区域通常是一组物理页面,但当使用大页面(通过 -XX:+UseLargePages)时,几个区域可能组成单个物理页面。

如果指定了 +XX:+UseNUMA 选项,那么在 JVM 初始化时,这些区域将在可用的 NUMA 节点总数中均匀分布。

在开始时为每个区域固定 NUMA 节点有点不够灵活,但可以通过以下增强来减轻这种情况。为了为变异线程分配新对象,G1 可能需要分配一个新区域。它会优先从当前线程绑定的 NUMA 节点中选择一个空闲区域,以便对象在年轻代中保持在相同的 NUMA 节点上。如果在为变异线程分配区域时相同的 NUMA 节点上没有空闲区域,G1 将触发垃圾收集。另一种待评估的想法是按照距离顺序搜索其他 NUMA 节点的空闲区域,从最近的 NUMA 节点开始。

我们不会试图在老年代中保持对象在相同的 NUMA 节点上。

超大区域不参与此分配策略。我们不会对这些区域进行特殊处理。

测试

使用 -XX:+UseNUMA 选项的现有测试应该能够发现任何正确性问题。我们假设测试使用 NUMA 硬件。

当关闭 NUMA 感知分配时,原始代码应该没有性能差异。

风险与假设

我们假设大多数短生命周期对象通常是由分配它们的线程访问的。这在大多数面向对象的程序中对于大多数短生命周期对象来说无疑是正确的。然而,有些程序并不完全符合这一假设,因此在某些情况下可能存在性能下降。此外,这种策略的好处还取决于底层系统 NUMA 程度的交互作用以及在这些系统上线程在 NUMA 节点之间迁移的频率,尤其是在负载较高的情况下。