在有效Java的这段示例代码中,如何防止JVM“优化所有内容”?

时间:2018-12-08 15:11:33

标签: java optimization jvm effective-java

我为Joshua Bloch的书《有效的Java》找到了this example code。旨在说明为什么应避免不必要地创建对象:

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方法的最后两行在这里完成了什么?

3 个答案:

答案 0 :(得分:2)

最后两行迫使编译器运行整个循环,以找到x的值。否则,它可能会检测到根本没有使用x并忽略该循环,因为它内部没有进行任何“实际”工作。即使sum()被反复调用,但是如果我们对x不执行任何操作,则最终将舍弃累积其返回值的结果。

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

答案 1 :(得分:2)

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

没人!

因此,编译器可能会假设:永远不会使用该值,并且写入该值的代码会产生节点副作用。没用的东西,为什么浪费时间写信给他们。

因此:最终的第一个“读取”用法可防止过度渴望的编译器“优化”对要测量的方法的调用!

答案 2 :(得分:0)

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

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

-Djava.compiler=NONE

准时:

  

JVM编译类文件时,它没有完成完整的类   文件;它仅根据需要编译一部分。这样可以避免沉重   完整的源代码解析。这种类型的编译称为   JIT或即时编译。 JVM是平台(OS)依赖的代码   生成JIT是面向平台的,生成本机字节码,   因此它比JVM更快:)