【问题标题】:Java memory model and local variable [closed]Java内存模型和局部变量
【发布时间】:2021-02-25 09:35:10
【问题描述】:

这个问题与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

【问题讨论】:

  • 这在很大程度上取决于案例、JVM 实现以及应用程序运行时的特定时间。可能是 JIT,也可能是字节码。。这个问题并不是很狭隘和具体。
  • 另请注意,“Java 内存模型”实际上是一个规范,主要处理多线程程序如何与内存交互,并没有准确描述您在这个问题中要求的内容(是的,这有点令人困惑,因为从概念上讲,您所询问的内容可以有效地描述为“Java 内存模型”,但该短语已经用于表示稍有不同的意思)。
  • 您真的在这里询问 Java 内存模型 (JMM) 吗? JLS 第 17.4 章?

标签: java java-memory-model


【解决方案1】:

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

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

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

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

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


1 - 在寄存器中、在 RAM 中、在高速缓存中、在硬件堆栈中……甚至可以写在沙发后面的一张纸上,如果你的硬件平台支持的话。支持>


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

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

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

我们可以高度肯定地说2的是,该实现将从某处 在这个例子中。

对于 Java,JLS 规定它必须返回最新的值。 (如果你想看的话,它在 JLS 17.4 中。)JLS 让 Java 实现来决定如何做。

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


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

【讨论】:

  • println 方法应该不断地从 RAM 中获取变量值,而不是从 cpu 缓存中获取。
  • 抱歉,这完全是错误的。您不会在 Java 语言规范中找到任何支持它的内容。正是 JLS 决定了 Java 程序 应该 的行为方式。
  • JLS(有效)要求使用最新的 RAM 值的唯一情况是 volatile 字段。
【解决方案2】:

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

【讨论】:

  • 应该补充一点,从概念上讲,堆栈是 RAM 的一部分,但各种优化实际上可能意味着堆栈上的变量不会在主内存中的任何位置结束(例如,如果 CPU 寄存器而是用于局部变量的整个生命周期)。
  • 虽然这是真的,但与提出的问题无关。
  • @eis 提出的问题是“println 方法在哪里获取 c 变量的值并将其打印在屏幕上?CPU 缓存或 RAM”。不过,我将添加 JMM 未对其进行监管的部分。
  • 我同意 eis。我对另一个问题有类似的评论。这个答案更详细地说明了所谓的堆和堆栈表示;而问题是关于最低级别的物理存储。
  • @GiorgiTsiklauri - 不正确。这个问题是关于 Java 内存模型的。 JMM根本不是关于物理存储的。至少,不是直接的。它实际上是关于共享变量更改的可见性......独立于物理存储。
猜你喜欢
  • 1970-01-01
  • 2012-01-29
  • 1970-01-01
  • 2012-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-02
  • 2023-03-19
相关资源
最近更新 更多