java - 为什么共享变量会缓存在CPU缓存中?

标签 java multithreading visibility shared-variable

我试图了解 Java 内存模型,但一直未能了解有关 CPU 缓存的要点。

据我所知,在 JVM 中我们有以下位置来存储本地变量和共享变量:

local variables -- on thread stack

shared variables -- in memory, but every CPU cache has a copy of it

所以我的问题是:为什么将局部变量存储在堆栈上,并将共享变量(缓存)存储在CPU缓存中?为什么不反过来(假设 CPU 缓存太昂贵而无法存储两者),我们将局部变量缓存在 CPU 缓存中,而只从内存中获取共享变量?这是Java语言设计的一部分还是计算机体系结构的一部分?

进一步:“CPU 缓存”听起来很简单,如果多个 CPU 共享一个缓存会怎样?而在具有多级缓存的系统中,共享变量的副本将存储在哪一级缓存中?此外,如果超过 1 个线程在同一个 CPU 核心中运行,是否意味着它们共享同一组缓存的共享变量,因此即使共享变量未定义 volatile,变量的访问对于同一 CPU 上运行的其他线程仍然立即可见?

最佳答案

“本地”和“共享”变量在代码上下文之外毫无意义。它们不会影响状态的缓存位置,甚至不会影响状态的缓存。思考或推理你的状态存储在哪里甚至没有用; JMM 存在的全部原因是这样的细节(这些细节因架构而异)不会暴露给程序员。通过依赖低级硬件细节,您会提出有关 JMM 的错误问题。它对您的应用程序没有用处,它会使应用程序变得脆弱、更容易损坏、更难以推理并且更不易移植。

也就是说,一般来说,您应该假设任何程序状态(如果不是所有状态)都有资格被缓存。事实上,缓存什么并不重要,任何东西都可以,无论是原始类型还是引用类型,甚至是由多个字段封装的状态变量。无论线程运行什么指令(这些指令也因体系结构而异 - 请注意!),这些指令默认返回 CPU 来确定哪些内容与缓存相关以及哪些内容不缓存;程序员自己不可能做到这一点(尽管它可能可能影响状态变量可能被缓存的位置,看看 false sharing 是什么)。

同样,我们还可以对 x86 进行一些更多的概括,即 Activity 的基本类型可能放在寄存器中,因为 P/ALU 将能够以最快的速度使用它们。不过其他的都可以。如果原语是核心本地的,则它们有可能被移动到 L1/2 缓存,当然它们可能会很快被覆盖。如果 CPU 认为将来会发生上下文切换,则可能会将状态变量放在共享的 L3 上,或者不会。硬件专家需要对此做出回应。

理想情况下,状态变量将存储在距离处理器单元最近的高速缓存(寄存器、L1/2/3,然后是主存储器)中。但这是由 CPU 决定的。在 Java 级别推理缓存语义是不可能的。即使启用了超线程(我不确定 AMD 等效项是什么),线程也不允许共享资源,即使如此,如果可以的话,请记住可见性并不是与共享状态变量相关的唯一问题;在处理器执行流水线操作的情况下,您仍然需要适当的指令来确保正确的顺序(即使在您摆脱 CPU 上的读/写缓冲之后),无论是 hwsync 还是适当的围栏或其他。

同样,推理缓存的属性是没有用的,因为 JMM 会为您处理该属性,而且因为无法确定缓存的位置/时间/内容。此外,即使您确实知道地点/时间/内容问题,您仍然无法推理数据可见性;无论如何,所有缓存都以相同的方式处理缓存数据,您将需要依赖处理器更新 ME(O)SI 状态之间的缓存状态、指令排序、加载/存储缓冲、回写/直写等。 .并且您还没有处理可能发生在操作系统和 JVM 级别的问题。同样,幸运的是,JDK 允许您使用基本工具,例如 volatilefinal 和原子,这些工具在所有平台上一致工作,并生成可预测且简单的代码(呃)来推理。

关于java - 为什么共享变量会缓存在CPU缓存中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47965438/

相关文章:

java - Android 应用程序在处理大文件时在某些设备上崩溃

Java列表序列化

java - Round Double 值和指数表示法 (java)

c# - C#异步-暂停多个任务,直到完成一个任务

.net - JS style.display 和.NET visible 的区别

c# - 编程语言(Java/C#/C++/Python/…)中不同构造的速度

JAVA按特定顺序唤醒线程

javascript - 如果元素不在顶部但实际上可见,是否可以在 javascript 中获取指定点的元素

c++ - 访问模板参数的 protected 成员

java - 如何在服务器模式下将 OpenOffice 用作多线程服务?