假设我有这个简单的方法:
static final Integer me = Integer.parseInt("2");
static int go() {
return me * 2;
}
对于 javac,me
不是常量(根据 JLS 规则),但对于 JIT 很可能是。
我尝试使用以下方法对此进行测试:
public class StaticFinal {
public static void main(String[] args) {
int hash = 0;
for(int i=0;i<1000_000;++i){
hash = hash ^ go();
}
System.out.println(hash);
}
static final Integer me = Integer.parseInt("2");
static int go() {
return me * 2;
}
}
并运行它:
java -XX:+UnlockDiagnosticVMOptions
-XX:-TieredCompilation
"-XX:CompileCommand=print,StaticFinal.go"
-XX:PrintAssemblyOptions=intel
StaticFinal.java
我不知道组装很好,但这很明显:
mov eax,0x4
的结果go
是立即的4
,即:JIT “信任”me
是一个常数,因此2 * 2 = 4
。
如果我删除static
并将代码更改为:
public class NonStaticFinal {
static NonStaticFinal instance = new NonStaticFinal();
public static void main(String[] args) {
int hash = 0;
for(int i=0;i<1000_000;++i){
hash = hash ^ instance.go();
}
System.out.println(hash);
}
final Integer me = Integer.parseInt("2");
int go() {
return me * 2;
}
}
并运行它:
java -XX:+UnlockDiagnosticVMOptions
-XX:-TieredCompilation
"-XX:CompileCommand=print,NonStaticFinal.go"
-XX:PrintAssemblyOptions=intel
NonStaticFinal.java
我确实在汇编中看到:
shl eax,1
这实际上是me
with的乘法2
,通过移位完成。所以 JIT 不相信me
是一个常数,这是意料之中的。
现在的问题。我想如果我添加TrustFinalNonStaticFields
标志,我会看到相同的mov eax 0x4
,即:运行:
java -XX:+UnlockDiagnosticVMOptions
-XX:-TieredCompilation
"-XX:CompileCommand=print,NonStaticFinal.go"
-XX:+UnlockExperimentalVMOptions
-XX:+TrustFinalNonStaticFields
-XX:PrintAssemblyOptions=intel
NonStaticFinal.java
应该显示mov eax,0x4
,但令我惊讶的是它没有,并且代码保持为:
shl eax,1
有人可以解释发生了什么以及我缺少什么吗?