0

是否有任何理由将调用非静态非构造方法的指令变成两条不同的指令而不是一条统一的指令,比如invokeinstance?它是否与一些随机的内部 JVM 机制有关,或者它是另一个可怕的遗留问题?

我知道我们有invokespecial,因为调用构造函数需要名称检查,标记另一个构造函数已执行等,并且invokestatic因为我们不需要将objectref转储到新的堆栈帧中。invokevirtual但是,Sun 选择将可能的通用指令拆分到and中并没有一个容易找到的理由invokeinterface。如果不拆分它,ASM 代码可能会简单得多,因为我们不必查看所有超级接口来查看这是否是接口方法,从而构建代码复杂性。

4

1 回答 1

2

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

考虑以下伪代码(注意这在 Java 中是不允许的,但 JVM 允许等效的字节码)

class A
class B extends A implements Foo

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

没有办法静态地知道 a 是否实现了 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 的类型为 be A,但未实现Foo. 因此,如果它试图静态检查接口作为验证时间,它将拒绝有效的 Java 代码!唯一可行的解​​决方案是不对接口进行类型检查。

为了安全地检查接口,JVM 必须能够推断出诸如“A 的子类也实现了 Foo”之类的类型,这显然给必须快速高效的东西增加了巨大的复杂性。所以设计师没有走这条路是有道理的。

PS Invokespecial 不仅仅用于构造函数——它也用于私有和超级方法调用。很可能它最初是作为优化的单独指令,因为调用的方法在加载时是已知的,而不是随目标的运行时类型而变化。事实上,它最初被称为invokenonvirtual。

于 2016-01-30T18:51:45.310 回答