Java如何实现多态性,基于itable, vtable源码分析

在Java实现中我们常使用多态性,在java里主要是通过itable, vtable来实现准确的跳转。

Vtable: 虚拟函数表

该类所有函数自有函数(除了static, final)和 父类的函数虚拟表。

结构:

vtableEntry | vtableEntry |vtableEntry...

是以vtableEntry 结构体的数组顺序结构,在每个entry 中保存了所调用的函数的指针

(源码参考klassVtable.hpp)

举个例子:

[java]  view plain copy
  1. class A   
  2. {  
  3. public void A(){  
  4. }  
  5. public void A(String a){  
  6. }  
  7. }  
  8.   
  9. Class B extends A{  
  10. public  void A(){  
  11. }  
  12. public void B(){  
  13.   
  14. }  
  15. }  

对于B的vtale: 就是 {B.A}{A.A(String)}{B.B}


如何初始化

当类初始化的时候,复制父类的虚拟表, 然后根据自己的顺序替换或者增加虚拟表的内容,

1. 如果overwrite函数,(方法名字,参数签名 完全一样),也就是替换虚拟表相同顺序的内容

2. 如果overload函数(方法名字,参数签名完全不一样)/或者自己写的函数,顺序添加到虚拟表尾部

3. 特殊的mirandas,该类的实现的接口函数里的方法列表,如果函数名字和参数签名一样的话,认为是同一个,顺序的添加到虚拟表的最后


Itable: 接口函数表

该类所有实现接口的函数列表, 相对来说这个结构复杂一点 

{itableOffsetEntry0}{itableOffsetEntry1}...{itableMethodEntry0}{itableMethodEntry1}

itableOffsetEntry 保存的是interface,和对应的itableMethodEntry的偏移

itableMethodEntry保存的是实现的函数

源码参考(klassVtable.hpp)

初始化

接口中非public的,和abstract的方法是不会加到itable中去。


Java中的函数调用

调用方式

java中函数常见调用方式

_invokeinterface调用接口函数
ex. interface A{ method b}
A.b 
_invokevirtual非private的一些方法
_invokespecialprivate,构造器的一些方法,(个人认为可以包含final函数)
_invokestaticstatic 方法

如何调用

根据前面的分析,我们可以知道 

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 函数编号多对应的方法。

举个例子:

[java]  view plain copy
  1. interface A {  
  2.  public void a();  
  3. }  
  4. class C implements A{  
  5. public void a(){  
  6. }  
  7. }  
  8. class B {  
  9.    public void test(A x){  
  10.        x.a();  
  11.     }  
  12. }  


x.a()的调用是invokeinterface

首先通过查找自己的栈,找到传过来的对象是C,然后查找 C的itable, 从itableOffsetEntry开始遍历,找到对应的class的位置,然后通过位移找到itableOffsetEntry对应的第一个itableMethodEntry,根据cache->f2的函数编号,找到对应的itableMethodEntry

具体实现参考 bytecodeInterpreter.cpp


为什么需要itable,而不是用vtable解决所有问题

一个类可以实现多个接口,而每个接口的函数编号是个自己相关的,vtable 无法解决多个对应接口的函数编号问题。而对继承一个类只能继承一个父亲,子类只要包含父类vtable,并且和父类的函数包含部分编号是一致的,就可以直接使用父类的函数编号找到对应的子类实现函数。


性能

接口调用, 多态调用,直接调用的性能也就可以显而易见了,invokeinterface 最慢,invokevitual中,invokespeical最快


本文转自 : http://blog.csdn.net/raintungli/article/details/8706028

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值