是否有任何理由不将invokevirtual和invokeinteface字节码指令合并为一个?

时间:2016-01-30 17:25:18

标签: java jvm bytecode invoke invokevirtual

是否有任何理由使指令将非静态非构造函数方法调用为两个不同的指令而不是一个统一指令,如invokeinstance?它与一些随机的内部JVM机制有什么关系,还是另一个可怕的遗留问题?

我知道我们有invokespecial因为调用构造函数需要名称ckecking,标记另一个构造函数已经执行等,而invokestatic因为我们不需要 objectref 转入新的堆栈框架。但是,它没有一个容易理解的原因,为什么Sun选择将可能的通用指令转化为invokevirtualinvokeinterface。在不拆分的情况下,ASM代码可以简单得多,因为我们不必查看所有的超接口,看看这是否是一个接口方法,从而构建代码的复杂性。

1 个答案:

答案 0 :(得分:2)

Invokeinterface是不同的,因为接口只在运行时进行类型检查。使用虚方法,您可以静态地确定类型是定义方法的类的子类型。对于接口,无法确定该值是否具有实现该接口的类型而不知道该值的运行时类型。

考虑以下伪代码(请注意,Java中不允许这样做,但JVM允许使用等效的字节码)

class A
class B extends A implements Foo

A a = new B()
a.fooMethod()

没有办法静态地知道是否实现了Foo,因为静态类型A没有实现Foo,但实际的运行时类型B没有。

编辑:上面的示例将被Java编译器拒绝,但不会被JVM拒绝。您可能想知道为什么JVM不仅仅应用与编译器相同的规则。不同之处在于JVM没有关于局部变量的源级别类型信息。请考虑以下示例,Java中允许这样做。

class A
class B extends A implements Foo
class C extends A implements Foo

Foo x = null;
if (whatever) {
x = new B();
} else {
x = new C();
}
x.fooMethod();

JVM不知道x的预期类型(没有stackmaptables,直到很久之后才会引入),因此它将x的类型推断为A,它不实现{{ 1}}。因此,如果它试图将接口静态检查为验证时间,它将拒绝有效的Java代码!唯一可行的解​​决方案是不进行类型检查接口。

为了安全地检查接口,JVM必须能够推断出类似“A的子类同时实现Foo”的类型,这显然增加了必须快速有效的大量复杂性。所以设计师没有走这条路是有意义的。

P.S。 Invokespecial不仅适用于构造函数 - 它还用于私有和超级方法调用。最有可能它最初是一个单独的指令作为优化,因为调用的方法在加载时已知,而不是随目标的运行时类型而变化。实际上,它最初被称为invokenonvirtual。