java - 将线程本地内存刷新到全局内存是什么意思?

标签 java memory model multithreading

我知道在 Java 中使用 volatile 变量的目的是写入这些变量立即对其他线程可见。我也知道同步块(synchronized block)的作用之一是将线程本地内存刷新到全局内存。

我从来没有完全理解在这种情况下对“线程本地”内存的引用。我知道仅存在于堆栈上的数据是线程本地的,但是当谈论堆上的对象时,我的理解变得模糊。

我希望得到对以下几点的评论:

  1. 在多处理器机器上执行时,刷新线程本地内存是否只是将CPU缓存刷新到RAM中?

  2. 在单处理器机器上执行时,这是否意味着什么?

  3. 如果堆有可能在两个不同的内存位置(每个由不同的线程访问)有相同的变量,在什么情况下会出现这种情况?这对垃圾收集有什么影响?虚拟机做这种事情有多积极?

  4. (编辑:添加问题 4) 退出同步块(synchronized block)时会刷新哪些数据?它是线程在本地拥有的一切吗?是否只有在同步块(synchronized block)内进行的写入?

    Object x = goGetXFromHeap(); // x.f is 1 here    
    Object y = goGetYFromHeap(); // y.f is 11 here
    Object z = goGetZFromHead(); // z.f is 111 here
    
    y.f = 12;
    
    synchronized(x)
    {
        x.f = 2;
        z.f = 112;
    }
    
    // will only x be flushed on exit of the block? 
    // will the update to y get flushed?
    // will the update to z get flushed?
    

总的来说,我想了解线程本地是指仅可由一个 CPU 物理访问的内存,还是虚拟机完成逻辑线程本地堆分区?

任何指向演示文稿或文档的链接都会非常有帮助。我花了很多时间研究这个,虽然我找到了很多不错的文献,但我无法满足我对线程本地内存的不同情况和定义的好奇心。

非常感谢。

最佳答案

您所说的刷新被称为“内存屏障”。这意味着 CPU 确保它所看到的 RAM 也可以从其他 CPU/内核中查看。这意味着两件事:

  • JIT 编译器刷新 CPU 寄存器。通常,代码可能会在 CPU 寄存器中保留一些全局可见数据(例如实例字段内容)的副本。从其他线程看不到寄存器。因此,synchronized 的一半工作是确保不维护这样的缓存。

  • synchronized 实现还执行内存屏障,以确保从当前内核对 RAM 的所有更改都传播到主 RAM(或者至少所有其他内核都知道这个核心具有最新的值(value)——缓存一致性协议(protocol)可能相当复杂)。

第二项工作在单处理器系统上是微不足道的(我的意思是,具有单核的单 CPU 系统),但如今单处理器系统往往变得越来越少。

对于线程局部堆,理论上可以做到这一点,但通常不值得付出努力,因为没有任何内容可以说明要使用 同步 刷新内存的哪些部分。这是线程共享内存模型的一个限制:所有内存应该是共享的。在第一次遇到 synchronized 时,JVM 应将其所有“线程本地堆对象”刷新到主 RAM。

然而最近来自 Sun 的 JVM 可以执行“逃逸分析”,其中 JVM 成功地证明某些实例永远不会从其他线程可见。这是典型的,例如,由 javac 创建的用于处理字符串连接的 StringBuilder 实例。如果实例从未作为参数传递给其他方法,则它不会变为“全局可见”。这使它有资格进行线程局部堆分配,甚至在适当的情况下,也可以进行基于堆栈的分配。请注意,在这种情况下没有重复;该实例不是“同时在两个地方”。只是 JVM 可以将实例保存在一个私有(private)的地方,这不会产生内存屏障的成本。

关于java - 将线程本地内存刷新到全局内存是什么意思?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2495654/

相关文章:

memory - Go 的精确 GC 是如何工作的?

model - Netlogo 中是否可以创建由补丁颜色定义的边界?

c# - 在不同的程序集上分布 Entity Framework 模型

php - CakePHP - 执行 hasMany 的正确方法

linux - 用户空间内存编辑程序

java - 使用浏览器启动小程序时出现问题

java - 在 Java 中抛出 ArithmeticException

java - 转换为 char 数组后 Long 变量出现意外值

c - ARM Cortex-M 内存访问

java - 如何解决导航栏有导航底部问题