将本地内存刷新到全局内存意味着什么?

时间:2010-03-22 20:52:24

标签: java memory model multithreading

我知道Java中volatile变量的目的是对其他线程立即可以看到对这些变量的写入。我也知道同步块的一个影响是将线程本地内存刷新到全局内存。

我从未完全理解在此上下文中对'thread-local'内存的引用。我知道只存在于堆栈上的数据是线程本地的,但是当谈论堆上的对象时,我的理解变得模糊。

我希望能够就以下几点发表意见:

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

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

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

  4. (编辑:添加问题4)退出同步块时刷新了哪些数据?它是本地线程的一切吗?它只是在同步块内写的吗?

    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?
    
  5. 总的来说,我想要了解线程局部是否意味着只有一个CPU可以物理访问的内存,或者VM是否进行了逻辑线程局部堆分区?

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

    非常感谢。

4 个答案:

答案 0 :(得分:6)

你所谈论的同花顺被称为“记忆障碍”。这意味着CPU确保从RAM中看到的内容也可以从其他CPU /内核中查看。它意味着两件事:

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

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

第二项工作在单处理器系统上是微不足道的(我的意思是,具有单核CPU的系统具有单核)但是单处理器系统现在往往变得更加罕见。

至于线程局部堆,理论上可以做到这一点,但通常不值得努力,因为没有什么能说明用synchronized刷新内存的哪些部分。这是带有共享内存的线程模型的限制:所有内存应该被共享。在第一次遇到synchronized时,JVM应该将其所有“线程局部堆对象”刷新到主RAM。

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

答案 1 :(得分:1)

如果另一个线程可以看到未同步的对象的内存当前内容,那么它实际上是一个实现细节。

当然,有一些限制,因为所有内存都不会保留重复,并且并非所有指令都被重新排序,但关键是如果底层JVM发现它是一种更优化的方法,那么它就有了选项。

问题是堆真的“正确”存储在主存储器中,但与访问CPU缓存或将值保存在CPU内的寄存器相比,访问主存储器的速度很慢。通过要求将值写入存储器(这是同步所做的,至少在释放锁定时),它强制写入主存储器。如果JVM可以自由地忽略它,它可以获得性能。

就一个CPU系统上会发生什么而言,即使在执行另一个线程时,多个线程仍然可以将值保存在缓存或寄存器中。无法保证在没有同步的情况下,某个值对另一个线程可见,但显然更有可能。当然,在移动设备之外,单CPU就像软盘一样,所以这不会是一个非常重要的考虑因素。

如需更多阅读,我建议Java Concurrency in Practice。这是一本关于这个主题的实用书。

答案 2 :(得分:1)

它不像CPU-Cache-RAM那么简单。这些都包含在JVM和JIT中,并且他们添加了自己的行为。

看看The "Double-Checked Locking is Broken" Declaration。这是关于为什么双重检查锁定不起作用的论文,但它也解释了Java内存模型的一些细微差别。

答案 3 :(得分:1)

一个突出所涉及问题的优秀文件是来自JavaOne 2009技术会议的PDF

This Is Not Your Father's Von Neumann Machine: How Modern Architecture Impacts Your Java Apps

By Cliff Click,Azul Systems; Brian Goetz,Sun Microsystems,Inc。