我正在阅读关于JVM如何调用方法的this article,我想我已经掌握了大部分内容。但是,我仍然无法理解invokeinterface
的必要性。
我理解它的方式,一个类基本上有一个方法的虚拟表,当调用一个方法invokevirtual
或invokeinterface
时,这个虚拟表被咨询。
然后,在接口上定义的方法和在基类上定义的方法之间有什么区别?为什么不同的字节码?
description of the instructions看起来非常相似。
该文章似乎声称,每次调用方法时,接口的方法表都可以具有“不同的偏移量”。我不明白的是为什么一个接口会有一个方法表,因为没有对象可以将接口作为它的实际类型。
我错过了什么?
答案 0 :(得分:89)
每个Java类都与虚拟方法表相关联,该表包含指向类的每个方法的字节码的“链接”。该表继承自特定类的超类,并针对子类的新方法进行了扩展。如,
class BaseClass {
public void method1() { }
public void method2() { }
public void method3() { }
}
class NextClass extends BaseClass {
public void method2() { } // overridden from BaseClass
public void method4() { }
}
结果表
BaseClass 1. BaseClass/method1() 2. BaseClass/method2() 3. BaseClass/method3() NextClass 1. BaseClass/method1() 2. NextClass/method2() 3. BaseClass/method3() 4. NextClass/method4()
注意,NextClass
的虚方法表如何保留BaseClass
表的条目顺序,只是覆盖它覆盖的method2()
的“链接”。
JVM的实现因此可以通过记住invokevirtual
将始终是此方法将被调用的任何对象的虚方法表中的第三个条目来优化对BaseClass/method3()
的调用。 / p>
使用invokeinterface
无法进行此优化。如,
interface MyInterface {
void ifaceMethod();
}
class AnotherClass extends NextClass implements MyInterface {
public void method4() { } // overridden from NextClass
public void ifaceMethod() { }
}
class MyClass implements MyInterface {
public void method5() { }
public void ifaceMethod() { }
}
此类层次结构导致虚拟方法表
AnotherClass 1. BaseClass/method1() 2. NextClass/method2() 3. BaseClass/method3() 4. AnotherClass/method4() 5. MyInterface/ifaceMethod() MyClass 1. MyClass/method5() 2. MyInterface/ifaceMethod()
如您所见,AnotherClass
在第五个条目中包含接口的方法,MyClass
在第二个条目中包含它。要在虚拟方法表中实际找到正确的条目,对具有invokeinterface
的方法的调用将始终必须搜索整个表,而没有机会获得invokevirtual
所做的优化样式。
还有一些其他差异,例如invokeinterface
可以与实际不实现接口的对象引用一起使用。因此,invokeinterface
必须在运行时检查表中是否存在方法并可能引发异常。如果您想深入了解该主题,我建议,例如,"Efficient Implementation of Java Interfaces: Invokeinterface Considered Harmless"。
答案 1 :(得分:1)
比较JVM Spec中的两个说明,第一个区别是invokevirtual
会在查找过程中检查方法的可访问性,而invokeinterface
则不会。{/ p>