Java 内存模型 - volatile 和 x86

Java memory model - volatile and x86

我正在尝试了解 java volatile 的内在特性及其语义,以及它到底层架构及其指令的转换。如果我们考虑以下博客和资源

fences generated for volatile, What gets generated for read/write of volatile and

这是我收集的:

我很难理解的是:Java does not emit LFENCE on x86 即读取 volatile 不会导致 LFENCE... 我知道 x86 的内存排序会阻止使用 lods/stored 对负载进行重新排序,因此第二个要点得到了处理。但是,我假设为了让该线程看到状态,应该发出 LFENCE 指令以保证在执行栅栏后的下一条指令之前耗尽所有 LOAD 缓冲区(根据 Intel 手册)。我知道 x86 上有 cahce 一致性协议,但易失性读取仍应耗尽缓冲区中的任何负载,不是吗?

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

X86提供TSO。因此,在硬件级别上,您可以免费获得以下障碍 [LoadLoad][LoadStore][StoreStore]。唯一缺少的是 [StoreLoad]。

A load 具有 acquire 语义

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,以下fences可以no-ops:

[LoadLoad][LoadStore][StoreStore]

所以唯一相关的是 [StoreLoad],这可以通过 MFENCElock addl %(RSP),0

来完成

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

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