跳到主要内容

JEP 142:减少指定字段上的缓存争用

概括

定义一种方法来指定对象中的一个或多个字段可能在处理器核心之间高度竞争,以便虚拟机可以安排它们不与可能独立访问的其他字段或其他对象共享缓存行。

描述

当两个不同核心使用的两个内存位置最终位于同一高速缓存行并且至少其中一个核心正在执行写入时,就会发生内存争用。对于高度竞争的内存位置,这可能是一个严重的性能和可扩展性问题。此增强功能的目的是避免内核之间的内存争用,至少在开发过程中我们可以轻松识别的字段上如此。

这个想法是在每个可能会发生争用的字段之前和之后添加填充,以确保没有其他字段(或其他对象)可以最终出现在同一缓存行上。在无法保证对象对齐的一般情况下,填充的大小需要与我们正在运行的计算机的缓存行的大小相同。如果可以保证特定对象的对齐,我们就可以减少所需的填充量。例如,如果始终保证对象的第一个字段位于缓存行的开头,那么我们只需在字段之前填充足够的空间即可确保该字段也位于缓存行的开头,并且然后在其后填充足够的内容以确保以下字段位于后续缓存行的开头。

通过向类引入足够的虚拟字段,可以在类加载时相当轻松地实现此填充。之后更改类布局将更具挑战性,特别是在分配该类的实例和/或对其某些方法进行 JIT 处理之后。

如果我们想减少由于这种填充而浪费的内存,我们必须确保特定的对象对齐。然而,这是一个更复杂的更改,除了类加载之外,还将涉及 JVM 的其他几个部分: 分配代码(以确保特定对象的分配正确对齐,并将此类对象标记为对齐,以便将来会保持对齐)、JIT 编译器(了解哪些分配需要对齐并发出正确分配操作的指令,或调用特殊的运行时方法)、GC(以确保任何需要对齐的对象)移动时保持对齐)等。鉴于对齐可能只会让我们减少由于填充而浪费的内存占用,并且假设大多数需要填充的对象并不多,我们可能会通过引入来获得递减的回报此对齐要求。

主要挑战是如何允许开发人员指定哪些字段可能会出现争用。一种通用的方法是使用注释(尽管这确实需要访问源代码)。这样,JVM 就可以以尽可能最好的方式处理指定的字段(即,通过仅填充它们,或者通过填充和对齐分配的组合,如前所述)。

如果减少源代码不可用的对象(例如标准库类的实例)上的缓存争用很重要,则可以为开发人员提供特殊的工厂方法,该方法将进行正确的对齐和填充,以便分配的对象不与其他对象共享缓存行。

影响

  • 性能/可扩展性:目标是提高多线程应用程序的性能并允许它们更好地扩展。填充对象确实意味着更高的内存使用量。