假设我有这个简单的方法:
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
乘以 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
有人可以解释一下正在发生什么以及我错过了什么吗?
ciField::trust_final_non_static_fields
以了解隐式信任的最终实例字段的标准(请注意,if
链底部为return TrustFinalNonStaticFields
)。 - Jorn Verneejava/lang
包含在JDK 14中的第一个内存访问API孵化器中。 我不知道为什么添加了java/lang
,但无论如何,似乎错过了清理j.l.String和盒子类的情况。 - Jorn Vernee