x86 架构上的 Java、易失性和内存障碍 [英] Java, volatile and memory barriers on x86 architecture

查看:45
本文介绍了x86 架构上的 Java、易失性和内存障碍的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这更像是一个理论问题.我不确定所有概念、编译器行为等是否都是最新的并且仍在使用中,但我想确认一下我是否正确理解了我正在尝试学习的一些概念.

This is more of a theoretical question. I'm not sure if all concepts, compiler behaviors, etc. are uptodate and still in use, but I'd like to have confirmation if I'm correctly understanding some concepts I'm trying to learn.

语言是 Java.

据我目前所了解,在 X86 架构上,StoreLoad 屏障(尽管用于实现它们的确切 CPU 指令)放置在 Volatile 写入之后,以使它们对其他线程中的后续 Volatile Reads 可见(因为 x86不保证较新的读取总是看到较旧的写入)(参考 http://shipilev.net/blog/2014/on-the-fence-with-dependencies/)

From what I've understood so far, on X86 architecture, StoreLoad barriers (despite the exact CPU instructions used to implement them) are put after Volatile writes, to make them visible to subsequent Volatile Reads in other threads (since x86 doesn't guarantee that newer reads always see older writes) (reference http://shipilev.net/blog/2014/on-the-fence-with-dependencies/)

现在从这里 (http://jpbempel.blogspot.it/2013/05/volatile-and-memory-barriers.html) 我明白了:

Now from here (http://jpbempel.blogspot.it/2013/05/volatile-and-memory-barriers.html) I see that:

public class TestJIT
{
    private volatile static int field1;
    private static int field2;
    private static int field3;
    private static int field4;
    private static int field5;
    private volatile static int field6;

    private static void assign(int i)
    {
        field1 = i << 1; // volatile
        field2 = i << 2;
        field3 = i << 3;
        field4 = i << 4;
        field5 = i << 5;
        field6 = i << 6; // volatile.
    }

    public static void main(String[] args) throws Exception
    {
        for (int i = 0; i < 10000; i++)
        {
            assign(i);
        }
        Thread.sleep(1000);
    }
}

生成的程序集只有在 field6 分配之后才具有 StoreLoad,而在 field1 分配之后没有,但是它也是可变的.

the resulting assembly has the StoreLoad only after field6 assignment, and not after field1 assignment which however is volatile as well.

我的问题:

1) 到目前为止我写的东西有意义吗?还是我完全误解了什么?

1) Does what I have written so far make sense? Or am I totally misinterpreting something?

2) 为什么编译器在 field1 volatile 赋值后会忽略 StoreLoad?这是优化吗?但它有一些缺点吗?例如,另一个线程在 field1 赋值后启动,可能仍然读取 field1 的旧值,即使它实际上已被更改?

2) Why is the compiler omitting a StoreLoad after field1 volatile assignment? Is this an optimization? But has it some drawbacks? For example, another thread kicking in after field1 assignment, might still read an old value for field1 even if it has been actually changed?

推荐答案

1) 到目前为止我写的东西有意义吗?还是我完全误解了什么?

1) Does what I have written so far make sense? Or am I totally misinterpreting something?

我认为你做对了一切.

2) 为什么编译器在 field1 volatile 赋值后会忽略 StoreLoad?这是优化吗?但它有一些缺点吗?

2) Why is the compiler omitting a StoreLoad after field1 volatile assignment? Is this an optimization? But has it some drawbacks?

是的,这是一种优化,但要做到正确非常棘手.

Yes, it's an optimization, but it's a pretty tricky one to get right.

Doug Lea 的 JMM Cookbook 实际上展示了推荐障碍的示例在两个连续的 volatile 存储的情况下,在每个存储之后都有 StoreLoad 有一个 StoreStore(x86 no-op) 在两个存储之间和 StoreLoad 仅在第二个之后.然而,Cookbook 指出,相关分析可以公平地参与.

Doug Lea's JMM Cookbook actually shows an example of the recommended barriers in the case of two consecutive volatile stores, and there are StoreLoads after each one of them there's a StoreStore (x86 no-op) between the two stores and a StoreLoad only after the second one. The Cookbook however notes that the related analysis can be fairly involved.

编译器应该能够证明在写入 field1 和写入 field6 之间的同步顺序中不会发生 volatile 读取>.如果 TestJIT 稍有更改,以便在另一个线程中同时执行相当数量的 volatile 加载,我不确定这是否可行(通过当前的 HotSpot JIT)时间.

The compiler should be able to prove that a volatile read cannot occur in the synchronization order between the write to field1 and the write to field6. I'm not sure if that's doable (by the current HotSpot JIT) if TestJIT was changed slightly so that a comparable amount of volatile loads is executed in another thread at the same time.

例如,另一个线程在 field1 赋值后启动,可能仍然读取 field1 的旧值,即使它已实际更改?

For example, another thread kicking in after field1 assignment, might still read an old value for field1 even if it has been actually changed?

如果 volatile 加载遵循同步顺序中的 volatile 存储,则不应允许发生这种情况.所以如上所述,我认为 JIT 可以逃脱它,因为它没有看到任何 volatile 加载正在完成.

That should not be allowed to happen, if that volatile load follows the volatile store in the synchronization order. So as mentioned above, I think that the JIT gets away with it, because it doesn't see any volatile loads being done.

更新

更改了 JMM Cookbook 示例的详细信息,因为 kRs 指出我将 StoreStore 误认为是 StoreLoad.答案的本质根本没有改变.

Changed the details around the JMM Cookbook example, as kRs pointed out that I've mistook a StoreStore for a StoreLoad. The essence of the answer was not changed at all.

这篇关于x86 架构上的 Java、易失性和内存障碍的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆