跳到主要内容

JEP 171:栅栏本质

概括

向类中添加三个内存排序内在sun.misc.Unsafe函数。

动机

JVM 没有宣传的机制来提供最初未设想的或 JSR 133 内存模型规范中未设想的内存排序。 (但例如在最近的 C11/C++11 规范中存在。)其中包括java.util.concurrent当前依赖于现有内在函数的未记录(且可能是瞬态)属性的其他低级库中使用的几种构造。

在 VM 级别添加这些方法允许 JDK 库使用来支持 JDK 8 功能,同时也为以后通过新java.util.concurrentAPI 导出基本功能提供了可能性。如果即将推出的模块化支持使得其他人无法访问这些方法,那么这对于允许人们开发非 JDK 低级库可能变得至关重要。

描述

这三种方法提供了一些编译器和处理器需要的三种不同类型的内存栅栏,以确保特定访问(加载和存储)不会重新排序。实际上,它们在效果上与现有的getXXXVolatileputXXXVolatileputOrderedXXX方法相同,只是它们实际上并不执行访问;他们只是确保订购。然而,它们在概念上有一个区别:根据当前的 JMM,某些语言级别的使用volatile可能会与非易失性变量的某些使用重新排序。但这在这里是不允许的。 (当前的内在函数中也不允许这样做,但这是基于内在函数与基于语言的易失性访问之间未记录的差异。)

这三种方法是:

/**
* Ensures lack of reordering of loads before the fence
* with loads or stores after the fence.
*/
void loadFence();

/**
* Ensures lack of reordering of stores before the fence
* with loads or stores after the fence.
*/
void storeFence();

/**
* Ensures lack of reordering of loads or stores before the fence
* with loads or stores after the fence.
*/
void fullFence();

虽然这三种方法没有任何“不安全”之处,但按照惯例,Unsafe目前包含用于每次使用的易失性和原子操作的相关方法,因此似乎是这些方法的最佳归宿。

javadoc 可以变得更加严格,尽管不能根据 JMM 进行指定,因为它不涵盖“每次使用的易失性”。因此,以这种简单的形式保留它们可能最能向目标用户传达意图。此外,可能不可能(因为可能存在现有的使用依赖性)同时明确并稍微削弱现有易失性访问内在函数的最低要求的重新排序属性。然而,栅栏内在函数的存在允许用户在需要时明确地获得这些效果。

热点实施

这三种方法可以根据c1、c2 和解释器/运行时可用的现有 、 和 fence 方法来实现,此外,如果适用,还可以抑制内部编译器重新排序和值acquire重用releasevolatile这不需要新的底层功能,但仍然需要添加和调整散布在 Hotspot 中的代码,以实现新的内在函数。省略这些,这是一个实现草图:

对于 c2,实现相当于省略 等方法中的所有访问代码inline_unsafe_access,只留下基于内存的栅栏的生成,以及CPUOrder在优化期间禁用重新排序的内部栅栏。 (在 的情况下fullFence,还应谨慎地将获取栅栏与完整栅栏一起包含在内,以覆盖现有 c2 代码依赖于 的存在来检测负载方面的易失性volatile的可能性。)MemBarAcquire

loadFence: {
insert_mem_bar(Op_MemBarCPUOrder);
insert_mem_bar(Op_MemBarAcquire);
}

storeFence: {
insert_mem_bar(Op_MemBarCPUOrder);
insert_mem_bar(Op_MemBarRelease);
}

fullFence: {
insert_mem_bar(Op_MemBarCPUOrder);
insert_mem_bar(Op_MemBarAcquire);
insert_mem_bar(Op_MemBarVolatile);
}

LIRGenerator对于 c1,可以使用以下操作定义新节点

loadFence:  { if (os::is_MP()) __ membar_acquire(); }
storeFence: { if (os::is_MP()) __ membar_release(); }
fullFence: { if (os::is_MP()) __ membar(); }

另外,对于这三者,请在以下位置禁用 GVN ValueMap

xxxxxFence: { kill_memory(); }

对于 C++ 运行时版本(在prims/unsafe.cpp)中,通过现有OrderAccess方法实现:

loadFence:  { OrderAccess::acquire(); }
storeFence: { OrderAccess::release(); }
fullFence: { OrderAccess::fence(); }

测试

Aleksey Shipilev 最近建立的用于折磨测试挥发性物质和原子的测试基础设施很容易适应,以获得栅栏隔离访问与挥发性物质相同的覆盖范围。

风险和假设

我们假设 Oracle 工程师将继续协助集成到 JDK 8。