什么样的优化JIT应用于while循环

Mar*_*tin 2 java performance jit jvm

我写下了这段代码:

public class Main {

    private boolean stopThread = false;
    private int counter = 0;

    public static void main(String[] args) throws InterruptedException {
        final Main main = new Main();

        new Thread(() -> {
            try {
                System.out.println("Start");
                Thread.sleep(100);
                System.out.println("Done");
                main.stopThread = true;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }).start();

        new Thread(() -> {
            while (!main.stopThread) {
                main.counter++;
            }
            System.out.println(main.counter);
        }).start();
        System.out.println("End");
    }
}
Run Code Online (Sandbox Code Playgroud)

当我运行它时,while循环将永远运行。我对此有点挣扎,我对应用于此代码的优化 JIT 类型感到困惑。

首先我认为这是具有知名度的问题stopThread变量,但即使它是一个真正的while循环应该有点晚停比我分配stopThreadtrue时(从第1线程CPU缓存得到刷新到主存储器),所以事实并非如此。看起来像 JIT 硬编码falsestopThread变量,如果它是真的,为什么这个变量在运行时不会以某种方式定期刷新?

显然,volatile关键字解决了这个问题,但它并没有回答我这个问题,因为volatile可以确保可见性并防止 JIT 进行多次优化。

更重要的是,当我将sleep时间更改为 1ms 时,第二个线程将正确终止,因此我很确定这与可变可见性无关。

更新:值得一提的是,countersleep时间设置为 1-10 毫秒时,我得到了非零值。

更新 2:另外,我可以说,-XX:+PrintCompilation如果sleep时间设置为 100 毫秒,while循环将被编译,并且On Stack Replacement发生了。

更新 3:可能这就是我要找的:https : //www.youtube.com/watch?v=ADxUsCkWdbE&feature=youtu.be&t=889。正如我所想的 - 这是 JIT 执行的“优化”之一,防止它的方法是将变量指定为volatile,或loadloadFence作为while循环中的第一行。

答案:正如@a​​pangin 所说:

这种优化是循环不变提升。JIT 允许将 stopThread 的负载移出循环,因为它可能假设 non-volatile 字段不会在外部发生变化,而且 JIT 还看到 stopThread 在循环内部不会发生变化。

Mic*_*nic 5

原因不是 JIT 优化。您的代码正在对共享变量进行非同步访问stopThread(我们称之为“标志”)。

本质上,在将标志设置为真值的线程与正在检查该值的另一​​个线程之间存在竞争条件。如果在进入循环之前将该标志设置为真,则代码完成。如果不是(比赛失败),循环将不确定地持续多久,因为 CPU 缓存保存了 falsey 值。当标志为 时volatile,它的值是从主内存而不是 CPU 缓存中读取的,并且循环最终会在标志设置线程完成睡眠后立即结束。