我正在研究以下java程序的反汇编
public class ASMPlayground {
private String bar;
public String getBar(){
return bar;
}
public void setBar(String bar) throws IllegalAccessException, InstantiationException {
String name = String.class.newInstance();
System.out.println(name);
}
public static void main(String[] args) {
}
}
以下字节码片段引起了我的注意,似乎次优
LDC Ljava/lang/String;.class
INVOKEVIRTUAL java/lang/Class.newInstance ()Ljava/lang/Object;
CHECKCAST java/lang/String
问题:
当要执行的方法依赖于对象引用时,使用InvokeVirtual。鉴于" Class"是final,newInstance()仅存在 在" Class"为什么不使用InvokeSpecial而不是InvokeVirtual?它不会更高效吗?
答案 0 :(得分:3)
InvokeSpecial
用于指定
超类,私有和实例初始化方法调用
规格:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.invokespecial
Class.newInstance
既不是超类方法调用也不是私有方法调用,也不是调用初始化方法。它也不是动态方法,InvokeDynamic
在这里无关。这两个指令都不能在这里使用,因为它会面对jvms。在创建jvms时,可以允许用更高性能的#34;替换某些指令。那些,但正如我在jvms中所看到的,它还没有完成。
它不会更高效吗?
JIT足够聪明,可以理解在这种情况下不需要遍历虚拟方法表,所以它不应该减慢执行速度。应该通过实际测试来比较实际表现,但我认为没有理由期待那里存在显着差异。
答案 1 :(得分:0)
目标方法或类是final
的事实永远不会使用另一个类/调用方法将已编译的表单更改为类。
这是由JLS第13章“二进制兼容性”强制执行的,§13.4.17, final
methods:
将声明为
final
的方法更改为不再声明final
不会破坏与预先存在的二进制文件的兼容性。
将声明为
final
的类更改为不再声明final
不会破坏与预先存在的二进制文件的兼容性。
显然,依赖目标方法final
性质的调用指令会违反此规范。
预期没有相关的性能影响。当符号引用必须解析为实际方法时(根据JVM的运行时表示),始终存在第一次开销。此时,如果存在一个好处,JVM还可以记录此方法或其声明类实际上是final
到调用指令的事实。现代JVM走得更远,例如利用非final
方法实际上没有被覆盖到相同效果的事实,尽管如果子类被加载并实例化,这些调用需要被去优化,这有一个重写方法。所以唯一的区别是final
修饰符可以保证永远不需要这种去优化。