【发布时间】:2016-06-23 08:11:53
【问题描述】:
我们知道,一些 JIT 允许对对象初始化重新排序,例如,
someRef = new SomeObject();
可以分解为以下步骤:
objRef = allocate space for SomeObject; //step1
call constructor of SomeObject; //step2
someRef = objRef; //step3
JIT 编译器可能会重新排序如下:
objRef = allocate space for SomeObject; //step1
someRef = objRef; //step3
call constructor of SomeObject; //step2
即step2和step3可以被JIT编译器重新排序。 尽管这在理论上有效重新排序,但我无法在 x86 平台下使用 Hotspot(jdk1.7) 重现它。
那么,Hotspot JIT 编译器是否有任何可以重现的指令重新排序?
更新: 我使用以下命令在我的机器(Linux x86_64,JDK 1.8.0_40,i5-3210M)上执行了test:
java -XX:-UseCompressedOops -XX:+UnlockDiagnosticVMOptions -XX:CompileCommand="print org.openjdk.jcstress.tests.unsafe.UnsafePublication::publish" -XX:CompileCommand="inline, org.openjdk.jcstress.tests.unsafe.UnsafePublication::publish" -XX:PrintAssemblyOptions=intel -jar tests-custom/target/jcstress.jar -f -1 -t .*UnsafePublication.* -v > log.txt
我可以看到该工具报告了以下内容:
[1] 5 ACCEPTABLE 对象已发布,至少有 1 个字段可见。
这意味着观察者线程看到了一个未初始化的 MyObject 实例。
但是,我没有看到像@Ivan 那样生成的汇编代码:
0x00007f71d4a15e34: mov r11d,DWORD PTR [rbp+0x10] ;getfield x
0x00007f71d4a15e38: mov DWORD PTR [rax+0x10],r11d ;putfield x00
0x00007f71d4a15e3c: mov DWORD PTR [rax+0x14],r11d ;putfield x01
0x00007f71d4a15e40: mov DWORD PTR [rax+0x18],r11d ;putfield x02
0x00007f71d4a15e44: mov DWORD PTR [rax+0x1c],r11d ;putfield x03
0x00007f71d4a15e48: mov QWORD PTR [rbp+0x18],rax ;putfield o
这里似乎没有编译器重新排序。
更新2: @Ivan 纠正了我。我使用错误的 JIT 命令来捕获汇编代码。修复此错误后,我可以获取以下汇编代码:
0x00007f76012b18d5: mov DWORD PTR [rax+0x10],ebp ;*putfield x00
0x00007f76012b18d8: mov QWORD PTR [r8+0x18],rax ;*putfield o
; - org.openjdk.jcstress.tests.unsafe.generated.UnsafePublication_jcstress$Runner_publish::call@94 (line 156)
0x00007f76012b18dc: mov DWORD PTR [rax+0x1c],ebp ;*putfield x03
显然,编译器进行了重新排序,导致发布不安全。
【问题讨论】:
-
您打算如何检测到这种情况发生?绝对不能允许任何此类优化泄漏到“用户领域”。
-
@Thilo A) 通过查看 HotSpot 源代码? B) java 内存模型允许许多重新排序,因此如果发生这种情况,可以从不同的线程中观察到这种情况
-
一般情况下,方法A)比较实用,我们可以观察JIT编译器生成的汇编代码。
-
@Thilo 是的 - 除非两个线程正确同步到构造的对象,否则另一个线程可能会看到处于部分构造状态的对象。无论是因为内存可见性(CPU 缓存等)还是因为 CPU 或 JIT 编译器(或其他任何东西)重新排序,都没有指定(在 JLS 的 JMM 部分中)。除非线程正确同步,否则 JVM 不需要保护程序免受这种明显的重新排序。 (您是否真的会在实践中看到这种重新排序是另一回事)
-
当然,我提到的重新排序是有效的,很抱歉错字说它是无效的。我只想重现由 JIT 编译器完成的重新排序,如果给定一个未正确同步的程序,它会影响另一个线程。
标签: java multithreading jvm jit jvm-hotspot