指令 | |
---|---|
invokeinterface | 调用接口方法,在运行时搜索一个实现了这个接口方法的类型 |
invokevirtual | 调用对象的实例方法,根据对象的实际类型进行分派 |
invokedynamic | 允许应用级别的代码来确定执行哪一个方法调用 |
invokevirtual
invokevirtual常用与动态分派,即调用子类重写的方法,这里不同于调用接口方法。动态分派使用了虚方法表实现,虚方法表中只会存放可以被重写的方法,而且通常不会包含接口方法。如果虚方法表中将父类的虚方法表中的内容放在最前面,新增加的方法在后面依次添加。那么,因为单继承的特点,一个方法在所有类的虚方法表中的位置是相同的,也就是说,父类的某个方法在虚方法表的第2项,那么它也一定在子类的虚方法表的第二项。另外,虚方法表是在运行时通过实际类型向上查找到实现类,然后将符号引用解析为直接引用。
invokeinterface
Java中可以实现多个接口,如果将接口方法放到虚方法表中,那么对于不同实现来说,它在虚方法表中的位置是不确定的。为了体现和invokevirtual
指令以及虚方法表的区别,使用了invokeinterface
指令以及接口方法表。例如,调用接口A的方法fun(),因为在类B和类C中fun()
方法的索引位置不一样,所以需要在接口方法表中查找fun
方法。
invokedynamic
无论是invokevirtual
还是invokeinterface
可以在运行时动态分派,但是分派的实现固定在JVM中,同时还会受到静态语言的特性的限制。因此为了支持动态语言引入invokedynamic
指令,invokedynamic
指令的思想是将方法分派的逻辑交给应用,nvokedynamic
指令会通过Bootstrap Method
创建一个CallSite
然后将其链接到一个MethodHandle
,最终执行的是MethodHandle
指向的方法。