我试图理解java volatile的内在函数及其语义,以及它对底层架构及其指令的转换。如果我们考虑以下博客和资源
fences generated for volatile,What gets generated for read/write of volatile和Stack overflow question on fences
这是我收集的内容:
我正在努力理解的是: Java不会在x86上发出LFENCE 即读取volatile不会导致LFENCE ....我知道x86的内存排序会阻止使用lods / stored对负载进行重新编写,因此需要注意第二个要点。但是,我假设为了使该状态可以被该线程看到,应该发出LFENCE指令以保证在执行栅栏后的下一条指令之前所有LOAD缓冲区都被耗尽(根据英特尔手册)。我理解x86上有cahce coherence协议,但是volatile读取仍然应该消耗缓冲区中的任何LOAD,不是吗?
答案 0 :(得分:5)
在x86上,缓冲区固定到缓存行。如果缓存行丢失,则不使用缓冲区中的值。因此,不需要围栏或排出缓冲区;它们包含的值必须是最新的,因为另一个核心无法在不首先使高速缓存行无效的情况下修改数据。
答案 1 :(得分:0)
X86提供TSO。因此,在硬件级别,您可以免费获得以下障碍[LoadLoad] [LoadStore] [StoreStore]。唯一缺少的是[StoreLoad]。
负载具有获取语义
r1=X
[LoadLoad]
[LoadStore]
商店具有发布语义
[LoadStore]
[StoreStore]
Y=r2
如果要先存储然后再加载,则最终结果是:
[LoadStore]
[StoreStore]
Y=r2
r1=X
[LoadLoad]
[LoadStore]
问题在于,加载和存储仍然可以重新排序,因此顺序不一致。这对于Java内存模型是必需的。防止这种情况的唯一方法是使用[StoreLoad]。
[LoadStore]
[StoreStore]
Y=r2
[StoreLoad]
r1=X
[LoadLoad]
[LoadStore]
并且最合乎逻辑的地方是将其添加到写入中,因为通常读取比写入更频繁。这样写就变成:
[LoadStore]
[StoreStore]
Y=r2
[StoreLoad]
由于X86提供了TSO,因此以下防护栏可以禁止操作:
[LoadLoad] [LoadStore] [StoreStore]
因此,唯一相关的是[StoreLoad],这可以通过MFENCE
或lock addl %(RSP),0
LFENCE和SFENCE与这种情况无关。 LFENCE和SFENCE用于弱排序的加载和存储(例如SSE的加载和存储)。
在X86上[StoreLoad]的作用是停止执行加载,直到耗尽存储缓冲区为止。这样可以确保在存储变为全局可见(已离开存储缓冲区并进入L1d)之后,加载在全局范围内可见(因此从内存/缓存中读取)。