为什么在调用class.NewInstance()时使用InvokeVirtual而不是InvokeSpecial?

时间:2017-03-05 15:25:16

标签: java jvm java-bytecode-asm jvm-hotspot

我正在研究以下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?它不会更高效吗?

2 个答案:

答案 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不会破坏与预先存在的二进制文件的兼容性。

同样§13.4.2. final Classes

  

将声明为final的类更改为不再声明final不会破坏与预先存在的二进制文件的兼容性。

显然,依赖目标方法final性质的调用指令会违反此规范。

预期没有相关的性能影响。当符号引用必须解析为实际方法时(根据JVM的运行时表示),始终存在第一次开销。此时,如果存在一个好处,JVM还可以记录此方法或其声明类实际上是final到调用指令的事实。现代JVM走得更远,例如利用非final方法实际上没有被覆盖到相同效果的事实,尽管如果子类被加载并实例化,这些调用需要被去优化,这有一个重写方法。所以唯一的区别是final修饰符可以保证永远不需要这种去优化。