前段时间一个有趣的question被问到:
Can (a == 1 && a == 2 && a == 3) evaluate to true in Java?
我决定证明使用 Java 8 Stream API(准确地说是并行流)是可行的。这是我在极少数情况下有效的代码:
class Race {
private static int a;
public static void main(String[] args) {
IntStream.range(0, 100_000).parallel().forEach(i -> {
a = 1;
a = 2;
a = 3;
testValue();
});
}
private static void testValue() {
if (a == 1 && a == 2 && a == 3) {
System.out.println("Success");
}
}
}
然后我想,也许是因为潜在的 JIT 编译器优化?因此,我尝试使用以下 VM 选项运行代码:
-Djava.compiler=NONE
我禁用了 JIT,成功案例的数量显着增加!
即时编译器如何优化并行流,以便优化可能影响上述代码执行?
最佳答案
流并不重要。同样的效果可以用两个简单的线程观察到,如 this answer .
当a
不是volatile
时,JIT 编译器可以优化(而且它确实做到了!)连续赋值。
a = 1;
a = 2;
a = 3;
转化为
a = 3;
此外,JIT 编译器还将 if (a == 1 && a == 2 && a == 3)
优化为 if (false)
然后安全地删除整个 testValue()
调用作为死代码。
让我们看看为 lambda 生成的程序集。
要打印编译后的代码,我使用 -XX:CompileCommand=print,Race::lambda$main$0
。
# {method} {0x000000001e142de0} 'lambda$main$0' '(I)V' in 'Race'
# parm0: rdx = int
# [sp+0x20] (sp of caller)
0x00000000052eb740: sub rsp,18h
0x00000000052eb747: mov qword ptr [rsp+10h],rbp ;*synchronization entry
; - Race::lambda$main$0@-1 (line 8)
0x00000000052eb74c: mov r10,76b8940c0h ; {oop(a 'java/lang/Class' = 'Race')}
0x00000000052eb756: mov dword ptr [r10+68h],3h ;*putstatic a
; - Race::lambda$main$0@9 (line 10)
0x00000000052eb75e: add rsp,10h
0x00000000052eb762: pop rbp
0x00000000052eb763: test dword ptr [3470000h],eax
; {poll_return}
0x00000000052eb769: ret
除了方法 prologue 和 eplilogue 之外,只有一条指令存储值 3:
mov dword ptr [r10+68h],3h ;*putstatic a
因此,一旦方法被编译,System.out.println
永远不会发生。当您看到“成功”时,那些罕见的情况发生在解释期间,此时代码尚未进行 JIT 编译。
关于java - 即时编译器如何优化 Java 并行流?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48688363/