实时编译器如何优化Java并行流?

Ole*_*hov 4 java parallel-processing jit jvm java-stream

前段时间,人们提出了一个有趣的问题:

Can (a == 1 && a == 2 && a == 3) evaluate to true in Java?
Run Code Online (Sandbox Code Playgroud)

我决定证明可以使用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");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我想,也许是因为潜在的JIT编译器优化?因此,我尝试使用以下VM选项运行代码:

-Djava.compiler=NONE
Run Code Online (Sandbox Code Playgroud)

我禁用了JIT,成功案例数量大幅增加!

实时编译器如何优化并行流,以便优化可能会影响上面的代码执行?

apa*_*gin 7

流不重要.只有两个简单的线程就可以观察到相同的效果,就像在这个答案中一样.

如果a不是volatile,JIT编译器可以优化(实际上是!)连续分配.

    a = 1;
    a = 2;
    a = 3;
Run Code Online (Sandbox Code Playgroud)

变成了

    a = 3;
Run Code Online (Sandbox Code Playgroud)

此外,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
Run Code Online (Sandbox Code Playgroud)

除了方法序言和eplilogue之外,只有一条指令存储值3:

  mov     dword ptr [r10+68h],3h  ;*putstatic a
Run Code Online (Sandbox Code Playgroud)

因此,一旦编译该方法,就System.out.println不会发生.当您看到"成功"时,极少数情况发生在解释期间,当代码尚未进行JIT编译时.

  • 为什么不能'a = 1; a = 2; a = 3;`被优化为`a = 3;`如果`a`是`volatile`? (2认同)
  • @shmosel严格地说,它可能*在这种特殊情况下被优化,尽管这样的优化不是开发人员或编译器编写者通常期望的`volatile`变量.在HotSpot JVM中,易失性存储始终是对主存储器的写入. (2认同)