在Effective Java的这段示例代码中,JVM是如何阻止'optimizing away everything'的?

How is the JVM prevented from 'optimizing away everything' in this piece of example code from Effective Java?

我找到了 this example code Joshua Bloch 的书 Effective Java。它旨在演示为什么您应该避免不必要地创建对象:

public class Sum {
    private static long sum() {
        Long sum = 0L;
        for (long i = 0; i <= Integer.MAX_VALUE; i++)
            sum += i;
        return sum;
    }

    public static void main(String[] args) {
        int numSets = Integer.parseInt(args[0]);
        long x = 0;

        for (int i = 0; i < numSets; i++) {
            long start = System.nanoTime();
            x += sum();
            long end = System.nanoTime();
            System.out.println((end - start) / 1_000_000. + " ms.");
        }

        // Prevents VM from optimizing away everything.
        if (x == 42)
            System.out.println();
    }
}

main 方法的最后两行在这里完成了什么?

最后两行强制编译器 运行 整个循环,以找到 x 的值。否则它可能会检测到 x 根本没有被使用并忽略循环,因为在它内部没有 "real" 工作正在进行。 sum()虽然被反复调用,但如果我们对x.

不做任何处理,其返回值的累加结果最终会被丢弃

当然,这是假设循环内的println()语句可以被安全地忽略,我不确定编译器是否可以做出这样的决定。那将是一个激进的编译器!

最后的比较是该变量的唯一用法。没有它,谁会关心那个变量中的值?

没人!

因此,编译器可能会假设:该值从未被使用过,并且写入它的代码具有节点副作用。没有用到的东西,何必浪费时间给它们写信。

因此:最后的第一个 "read" 用法可防止编译器过度急于 "optimizing out" 调用您打算测量的方法!

最后两行将确保 JVM 不会删除调用,否则它可能会考虑无操作,一种选择是使用结果 - 因此您可以对所有 return 值求和然后在最后显示总和。

另一种防止 vm 优化的方法是禁用 JIT:

-Djava.compiler=NONE

JIT:

When JVM compiles the class file, it doesn’t complete the full class file; it compiles only a part of it on need basis. This avoids heavy parsing of complete source code. This type of compilation is termed as JIT or Just-In-Time compilation. JVM is Platform(OS) dependent Code generation JIT is Platform Oriented, generates the native byte code, so it is faster one than JVM :)