Java 内存模型 - 易失性和 x86

我试图理解java易失性及其语义的内在,以及它对底层架构及其指令的转换。如果我们考虑以下博客和资源

为易失性生成的栅栏,为栅栏上的易失性和堆栈溢出问题的读/写生成的内容

这是我收集到的:

  • 易失性读取在其后插入 loadStore/LoadLoad 屏障(x86 上的 LFENCE 指令)
  • 它防止了加载与后续写入/加载的重新排序
  • 它应该保证加载由其他线程修改的全局状态,即在LFENCE之后,其他线程完成的状态修改对其CPU上的当前线程可见。

我正在努力理解的是:Java不会在x86上发出LFENCE,即读取易失性不会导致LFENCE.我知道x86的内存排序可以防止使用lods/存储来重新排序负载,因此处理了第二个项目符号点。但是,我假设为了使此线程能够看到状态,应该发出LFENCE指令,以确保在执行围栏后的下一个指令之前,所有LOAD缓冲区都被耗尽(根据英特尔手册)。我知道x86上有cahce一致性协议,但易失性读取仍然应该耗尽缓冲区中的任何LOAD,不是吗?


答案 1

在 x86 上,缓冲区固定到缓存行。如果缓存行丢失,则不会使用缓冲区中的值。因此,无需围栏或排空缓冲区;它们包含的值必须是最新的,因为另一个内核无法在不首先使缓存行无效的情况下修改数据。


答案 2

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,因此以下围栏可以是无操作的:

[负载负载][加载存储][商店商店]

因此,唯一相关的是[StoreLoad],这可以通过一个或一个来完成MFENCElock addl %(RSP),0

LFENCE和SFENCE与这种情况无关。LFENCE 和 SFENCE 用于弱有序负载和存储(例如 SSE 的负载和存储)。

[StoreLoad] 在 X86 上执行的操作是停止执行加载,直到存储缓冲区被耗尽。这将确保在存储变得全局可见(已离开存储缓冲区并进入L1d)之后,负载是全局可见的(因此从内存/缓存中读取)。