Java内存模型和局部变量

问题描述

这个问题与java内存模型有关。

我有一个java方法

public class DataUtil{
  public void process(){
    int c=0;
    c=c+1;

    System.out.println(c);
  }
}

在“System.out.println(c)”行中, println 方法在哪里获取 c 变量的值并将其打印在屏幕上? cpu 缓存或 RAM

解决方法

需要明确的是,这个问题和这个答案仅与一个线程的程序的行为有关。我下面说的很多事情可能不适用于多线程程序。

println 方法在哪里获取 c 变量的值并将其打印在屏幕上? CPU 缓存或 RAM

Java 内存模型 (JLS 17.4) 对缓存和 RAM 没有明确说明。通常,它指定可见性行为,而不规定特定编译器实现该行为的方式。 JMM 要求当线程写入变量并随后读取 is 时存在 happens before 关系。发生在约束生成的代码以某种方式运行之前。然而,它并没有强制要求任何特定的实现方法来实现这些限制。

在您的示例中,JMM 甚至没有对值是来自缓存还是来自 RAM 设置隐式约束。 c 变量只能被一个线程访问。 (这是一个局部变量!)。因此编译器可以(理论上)将变量的值存储在任意位置1。唯一的限制是在访问变量时使用最新的值。编译器只需要跟踪保存最新变量的位置...

作为一般规则,JMM 只对不同线程共享的变量和对象有一些有趣的说法。


1 - 在寄存器中,在 RAM 中,在缓存中,在硬件堆栈中......甚至写在一张纸上,如果你的硬件平台支持的话。

上>

如果您在更广泛的意义上使用“记忆模型”,那么简单的答案是“我们不能说”。

  • 在内存模型是根据主内存和缓存指定的语言(不是 Java!)中,内存模型很可能不会限制这个例子。

  • 如果您不是在谈论任何特定的内存模型,而只是询问该值是从缓存还是从 RAM 中获取,那么我们不能说...因为这是一个实现细节语言的实现。例如,JVM。

我们可以肯定地说2是该实现将从获取并打印c的最新值在这个例子中的某个地方

对于 Java,JLS 说它必须返回最新的值。 (如果您想查看,它在 JLS 17.4 中。)JLS 将其留给 Java 实现来决定如何执行此操作。

可以安全地假设任何 JVM 实现都会提出一个可靠的解决方案;即将使用变量的最新值。但是弄清楚细节将是一项艰巨的任务......而且(IMO)不值得付出努力。 (您无需了解沃尔沃 264 自动变速箱的内部结构即可驾驶汽车。)


2 - 我们可以肯定,因为没有大量的单线程应用程序由于读取和写入变量的问题而无法工作的错误报告。此外,如果有任何疑问,可以检查 JIT 编译器源代码以了解它的作用,或者分析它生成的本机代码。

,

Java 内存模型并未对此进行规范 - 它专注于多线程程序的行为。
局部变量在堆栈上分配。函数的参数,例如 println,也通过堆栈传递 - 它们在调用之前被推送到堆栈顶部(根据调用约定)。这就是字节码中发生的事情,尽管 JIT 编译器或解释器也可能使用 CPU 寄存器,而不使用 RAM 中的堆栈。