当前位置: 首页 > 知识库问答 >
问题:

Java内存模型-volatile和x86

龚彬
2023-03-14

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

生成的栅栏的易失性,什么得到生成的读/写的易失性和堆栈溢出问题上的栅栏

以下是我收集的信息:

  • volatile read在其后面插入loadStore/LoadLoad屏障(x86上的LFENCE指令)
  • 它可以防止在后续写入/加载时对加载进行重新排序
  • 它应该保证加载由其他线程修改的全局状态,即在LFENCE之后,其他线程所做的状态修改对其CPU上的当前线程可见

我很难理解的是:Java在x86上不会发出LFENCE,即volatile的读取不会导致LFENCE。。。。我知道x86的内存排序阻止了LOD/stored加载的重新排序,所以第二个要点得到了解决。但是,我假设,为了使该线程可以看到状态,应该发出LFENCE指令,以确保在执行Geofence后的下一条指令之前(根据英特尔手册),所有加载缓冲区都已耗尽。我知道x86上有cahce一致性协议,但volatile read仍应耗尽缓冲区中的任何负载,不是吗?

共有2个答案

岳玉堂
2023-03-14

X86提供TSO。因此,在硬件层面上,您可以免费获得以下障碍[加载][加载存储][存储存储]。唯一缺少的是[存储负载]。

一群人学会了语义学

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,所以以下栅栏可以是无操作的:

加载加载加载存储存储存储

因此,唯一相关的是[存储负载],这可以通过一个MFENCE或一个lock addl%(RSP),0来实现

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

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

严恩
2023-03-14

在x86上,缓冲区固定到缓存线。如果缓存线丢失,则不会使用缓冲区中的值。因此,无需对缓冲区进行隔离或排空;它们包含的值必须是当前值,因为如果不先使缓存线无效,另一个核心无法修改数据。

 类似资料:
  • 本文向大家介绍讲一下volatile涉及的Java内存模型?相关面试题,主要包含被问及讲一下volatile涉及的Java内存模型?时的应答技巧和注意事项,需要的朋友参考一下 在 JDK1.2 之前,Java的内存模型实现总是从主存(即共享内存)读取变量,是不需要进行特别的注意的。而在当前的 Java 内存模型下,线程可以把变量保存本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写。这就可

  • 从实践中的Java并发性来看: 当一个字段被声明为volatile时,编译器和运行时会注意到这个变量是共享的,对它的操作不应该与其他内存操作一起重新排序。易失性变量不缓存在寄存器或缓存中,在这些寄存器或缓存中,它们对其他处理器隐藏,因此读取易失性变量总是返回任何线程最近的写入。(第25页) 和 Final字段不能修改(尽管它们引用的对象可以修改,如果它们是可变的),但它们在Java内存模型下也有特

  • 规范了Java虚拟机与计算机内存是如何协调工作的,规定了一个线程如何及何时能看到其他线程修改过的共享变量,在必须时如何同步地访问共享变量,控制线程本地内容和共享内容之间的同步。 2. 同步八种操作 操作 定义 lock(锁定) unlock(解锁) read(读取) load(载入) use(使用) assign(赋值) store(存储) write(写入) 3. 同步规则 Read和Load之

  • 使用包含Scala和Akka在内的Typesafe平台的主要好处是它简化了并发软件的编写过程。本文将讨论Typesafe平台,尤其是Akka是如何在并发应用中访问共享内存的。 Java内存模型 在Java 5之前,Java内存模型(JMM)定义是有问题的。当多个线程访问共享内存时很可能得到各种奇怪的结果,例如: 一个线程看不到其它线程所写入的值:可见性问题 由于指令没有按期望的顺序执行,一个线程观

  • 一、Java内存区域 方法区(公有): 用户存储已被虚拟机加载的类信息,常量,静态常量,即时编译器编译后的代码等数据。异常状态 OutOfMemoryError 其中包含常量池:用户存放编译器生成的各种字面量和符号引用。 堆(公有): 是JVM所管理的内存中最大的一块。唯一目的就是存放实例对象,几乎所有的对象实例都在这里分配。Java堆是垃圾收集器管理的主要区域,因此很多时候也被称为“GC堆”。异

  • (译注:这一个item有相当深的理论深度,原文也比较晦涩难懂,翻译者提醒大家,最好参照原文理解,如果翻译中有什么不恰当的地方,还请批评指出,不胜感谢。) 所谓“内存模型”,是计算机(硬件)体系结构与编译器双方之间的一种约定。有了它,大多数程序员便不用处处考虑日新月异的计算机硬件细节。如果没有内存模型,那么线程机制、锁机制及无锁编程等都无从谈起。 内存模型的最关键保证是:两个线程可以各自独立地存取各