【问题标题】:How is the JVM prevented from 'optimizing away everything' in this piece of example code from Effective Java?在这段来自 Effective Java 的示例代码中,如何防止 JVM “优化所有内容”?
【发布时间】:2018-12-08 15:11:33
【问题描述】:

我为 Joshua Bloch 的书《Effective 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方法的最后两行在这里完成了什么?

【问题讨论】:

  • 现在,我称之为超级快速的复出 ;-)

标签: java optimization jvm effective-java


【解决方案1】:

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

没人!

因此,编译器可能会假设:该值从未使用过,并且写入它的代码具有节点副作用。不习惯的东西,为什么要浪费时间给他们写信。

因此:最终的第一次“读取”用法可防止过度急切的编译器“优化”对您打算测量的方法的调用!

【讨论】:

    【解决方案2】:

    最后两行强制编译器运行整个循环,以找到x 的值。否则,它可能会检测到 x 根本没有被使用并忽略循环,因为在它内部没有进行“真正的”工作。即使sum()被反复调用,如果我们对x不做任何事情,其返回值的累加结果最终还是会被丢弃。

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

    【讨论】:

      【解决方案3】:

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

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

      -Djava.compiler=NONE
      

      JIT:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-12-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-15
        • 2016-01-01
        相关资源
        最近更新 更多