JVM 优化如何基于假设?

Ngu*_*anh 1 java jit jvm

在第 12.3.3. 节“不切实际的代码路径采样”中,Java 并发实践书说:

在某些情况下,JVM 可能会基于可能只是暂时正确的假设进行优化,如果它们变得不正确,则稍后通过使已编译的代码无效来回退它们

我无法理解上面的说法。

  1. 这些 JVM 假设是什么?
  2. JVM 如何知道假设是真还是假?
  3. 如果假设不真实,它会影响我的数据的正确性吗?

Mar*_*o13 6

你引用的语句有一个脚注,给出了一个例子:

例如,如果当前加载的类没有覆盖该方法,JVM 可以使用单态调用转换将虚拟方法调用转换为直接方法调用,但如果随后加载了覆盖该方法的类,则它会使编译的代码无效。

这里的细节非常、非常非常复杂。所以下面是一个极其简单化的例子。

想象一下你有一个界面:

 interface Adder { int add(int x); }
Run Code Online (Sandbox Code Playgroud)

该方法应该向 中添加一个值x,并返回结果。现在想象有一个程序使用这个类的实现:

class OneAdder implements Adder { 
    int add(int x) {
        return x+1;
    }
}

class Example {

    void run() {
        OneAdder a1 = new OneAdder();
        int result = compute(a1);
        System.out.println(result);
    }

    private int compute(Adder a) {
        int sum = 0;
        for (int i=0; i<100; i++) {
            sum = a.add(sum);
        }
        return sum;
    }
}
Run Code Online (Sandbox Code Playgroud)

在这个例子中,JVM 可以做一些优化。一个非常低级的方法是它可以避免使用vtable来调用该add方法,因为在给定的程序中该方法只有一个实现。但它甚至可以更进一步,并内联这个唯一的方法,因此该compute方法本质上变成了这样:

private int compute(Adder a) {
    int sum = 0;
    for (int i=0; i<100; i++) {
        sum += 1;
    }
    return sum;
}
Run Code Online (Sandbox Code Playgroud)

原则上,即使这样

private int compute(Adder a) {
    return 100;
}
Run Code Online (Sandbox Code Playgroud)

但是 JVM 也可以在运行时加载类。所以可能会有这样的情况,这个优化已经做了,后来,JVM加载了这样一个类:

class TwoAdder implements Adder { 
    int add(int x) {
        return x+2;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,对方法进行的优化compute可能会变得“无效”,因为不清楚它是用 aOneAdder还是 a调用的TwoAdder。在这种情况下,必须取消优化。

这应该回答1.你的问题。

关于2.:当然,JVM 会跟踪所有已完成的优化。它知道它已经add基于该方法只有一个实现的假设内联了该方法。当它找到此方法的另一个实现时,它必须撤消优化。

关于3.:当假设真时进行优化。当它们变得不真实时,优化被撤销。所以这不会影响你程序的正确性


更新:

同样,上面的例子非常简化,参考了书中给出的脚注。更多关于JVM的优化技术,可以参考https://wiki.openjdk.java.net/display/HotSpot/PerformanceTechniques。具体来说,推测性(基于配置文件)技术可能被认为主要基于“假设”——即基于迄今为止收集的配置文件数据做出的假设。