JVM源码系列:java如何实现多态性,基于itable, vtable源码分析
Posted raintungli
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM源码系列:java如何实现多态性,基于itable, vtable源码分析相关的知识,希望对你有一定的参考价值。
在Java实现中我们常使用多态性,在java里主要是通过itable, vtable来实现准确的跳转。
Vtable: 虚拟函数表
该类所有函数自有函数(除了static, final)和 父类的函数虚拟表。
结构:
vtableEntry | vtableEntry |vtableEntry...
是以vtableEntry 结构体的数组顺序结构,在每个entry 中保存了所调用的函数的指针
(源码参考klassVtable.hpp)
举个例子:
class A
public void A()
public void A(String a)
Class B extends A
public void A()
public void B()
对于B的vtale: 就是 B.AA.A(String)B.B
如何初始化
当类初始化的时候,复制父类的虚拟表, 然后根据自己的顺序替换或者增加虚拟表的内容,
1. 如果overwrite函数,(方法名字,参数签名 完全一样),也就是替换虚拟表相同顺序的内容
2. 如果overload函数(方法名字,参数签名完全不一样)/或者自己写的函数,顺序添加到虚拟表尾部
3. 特殊的mirandas,该类的实现的接口函数里的方法列表,如果函数名字和参数签名一样的话,认为是同一个,顺序的添加到虚拟表的最后
Itable: 接口函数表
该类所有实现接口的函数列表, 相对来说这个结构复杂一点
itableOffsetEntry0itableOffsetEntry1...itableMethodEntry0itableMethodEntry1
itableOffsetEntry 保存的是interface,和对应的itableMethodEntry的偏移
itableMethodEntry保存的是实现的函数
源码参考(klassVtable.hpp)
初始化
接口中非public的,和abstract的方法是不会加到itable中去。
Java中的函数调用
调用方式
java中函数常见调用方式
_invokeinterface | 调用接口函数 ex. interface A method b A.b |
_invokevirtual | 非private的一些方法 |
_invokespecial | private,构造器的一些方法,(个人认为可以包含final函数) |
_invokestatic | static 方法 |
如何调用
根据前面的分析,我们可以知道
invokeinterface 使用的是itable
invokevitual 使用的是vtable
invokesepical 直接调用不需要转换
invokestatic 直接调用不需要转换
jvm里面查找到正确的函数的方式
如果不是vitual,interface 调用,可以直接用cache->f1取到真实的函数
如果不是的话,
函数可以通过查找Java的栈,能找到具体执行的对象的所对应的class, 而对应的cache->f2 标识的是执行的函数编号。
如果是vitual 查找对应的vtable里的cache->f2 的函数编号, 这就是为什么vtable 首先先复制父对象的table。
如果是interface的调用,则使用itable。首先先遍历到对应的interface, 然后在通过interface 找到cache->f2 函数编号多对应的方法。
举个例子:
interface A
public void a();
class C implements A
public void a()
class B
public void test(A x)
x.a();
x.a()的调用是invokeinterface
首先通过查找自己的栈,找到传过来的对象是C,然后查找 C的itable, 从itableOffsetEntry开始遍历,找到对应的class的位置,然后通过位移找到itableOffsetEntry对应的第一个itableMethodEntry,根据cache->f2的函数编号,找到对应的itableMethodEntry
具体实现参考 bytecodeInterpreter.cpp
为什么需要itable,而不是用vtable解决所有问题
一个类可以实现多个接口,而每个接口的函数编号是个自己相关的,vtable 无法解决多个对应接口的函数编号问题。而对继承一个类只能继承一个父亲,子类只要包含父类vtable,并且和父类的函数包含部分编号是一致的,就可以直接使用父类的函数编号找到对应的子类实现函数。
性能
接口调用, 多态调用,直接调用的性能也就可以显而易见了,invokeinterface 最慢,invokevitual中,invokespeical最快
以上是关于JVM源码系列:java如何实现多态性,基于itable, vtable源码分析的主要内容,如果未能解决你的问题,请参考以下文章
JVM源码系列:ThreadMXBean 打出堆栈信息原理分析