在第 12.3.3. 节“不切实际的代码路径采样”中,Java 并发实践书说:
在某些情况下,JVM 可能会基于可能只是暂时正确的假设进行优化,如果它们变得不正确,则稍后通过使已编译的代码无效来回退它们
我无法理解上面的说法。
你引用的语句有一个脚注,给出了一个例子:
例如,如果当前加载的类没有覆盖该方法,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。具体来说,推测性(基于配置文件)技术可能被认为主要基于“假设”——即基于迄今为止收集的配置文件数据做出的假设。
| 归档时间: |
|
| 查看次数: |
166 次 |
| 最近记录: |