JEP 142:减少特定字段上的缓存争用
概述
定义一种方式,用于指定对象中的一个或多个字段可能在处理器核心之间存在高度争用,以便虚拟机(VM)可以安排这些字段不与其他可能被独立访问的字段或其他对象共享缓存行。
描述
当两个不同的核心使用的两个内存位置最终位于同一缓存行上,并且至少有一个核心正在执行写操作时,就会发生内存争用。对于高度争用的内存位置,这可能会成为一个严重的性能和可扩展性问题。此增强功能的目标是避免核心之间的内存争用,至少在我们可以在开发过程中轻松识别的字段上如此。
其思想是在每个可能发生争用的字段前后添加填充,以确保其他字段(或其他对象)不会最终位于同一缓存行上。在无法保证对象对齐的一般情况下,填充的大小需要与我们所运行机器的缓存行大小相同。如果可以保证特定的对象对齐,则可以减少所需的填充量。例如,如果一个对象的第一个字段始终保证位于缓存行的起始位置,那么我们只需要在该字段前填充足够的空间以确保该字段也位于缓存行的起始位置,然后在其后填充足够的空间以确保下一个字段位于下一个缓存行的起始位置。
通过在类中引入足够多的虚拟字段,可以在类加载时合理地轻松实现这种填充。而在之后更改类布局会更具挑战性,特别是当该类的实例已被分配和/或其某些方法已被 JIT 编译后。
如果我们想要减少由于这种填充而浪费的内存,就需要确保特定的对象对齐。然而,这是一个涉及面更广的变更,除了类加载之外,还会触及 JVM 的其他几个部分:分配代码(以确保特定对象的分配正确对齐,并且标记这些对象为已对齐,以便将来保持对齐)、JIT 编译器(以了解哪些分配需要对齐,并发出正确的分配操作指令,或调用特殊的运行时方法)、GC(以确保任何需要对齐的对象在移动时仍然保持对齐)等等。鉴于对齐可能只会让我们减少由于填充而浪费的内存占用,并且假设大多数需要填充的对象数量不多,通过引入这种对齐要求可能会导致收益递减。
主要的挑战在于如何让开发者指定哪些字段可能会遇到竞争。实现这一目标的一种通用方法是使用注解(尽管这确实需要访问源代码)。通过这种方式,JVM 可以尽可能以最佳方式处理这些指定的字段(即,要么仅填充它们,要么结合填充和前面讨论过的对齐分配方式)。
如果减少无法获取源代码的对象(例如标准库类的实例)上的缓存争用非常重要,那么可以为开发者提供一个特殊的工厂方法,该方法将执行正确的对齐和填充,以确保分配的对象不会与其他对象共享缓存行。
影响
- 性能/可扩展性:目标是提高多线程应用程序的性能,并允许它们更好地扩展。填充对象确实意味着更高的内存使用率。