klassVtable与klassItable类用来实现Java方法的多态,也可以叫动态绑定,是指在应用执行期间通过判断接受对象的实际类型,根据实际类型调用对应的方法。C++为了实现多态,在对象中嵌入了虚函数表vtable,通过虚函数表来实现运行期的方法分派,这在之前介绍HotSpot的二分模型时简单介绍过,这里不再介绍C++的方法分派。

1、klassVtable类

C++中的vtable只包含虚函数,非虚函数在编译期就已经解析出正确的方法调用了。Java vtable除了虚方法外还包含了其他的非虚方法。

vtable中的一条记录用vtableEntry表示,该类在klassVtable.hpp文件中定义,只定义了一个属性Method* _method,所以说此类只是对Method*做了简单包装而已,提供了操作Method对象的方法。

访问vtable需要通过klassVtable类,该类在klassVtable.hpp文件中定义,提供了操作vtable的方法,如Method* method_at(int i)、int index_of(Method* m)等,其实现都是基于vtable的内存起始地址和内存偏移完成的。

klassVtable类的定义及属性的声明如下:

A klassVtable abstracts the variable-length(可变长度) vtable that is embedded in InstanceKlass and ArrayKlass.
klassVtable objects are used just as convenient transient accessors to the vtable,not to actually hold the vtable data.
Note: the klassVtable should not be accessed before the class has been verified(until that point, the vtable is uninitialized). Currently a klassVtable contains a direct reference to the vtable data, and is therefore not preserved across GCs.
class klassVtable : public ResourceObj {
KlassHandle _klass; // my klass
int _tableOffset; // offset of start of vtable data within klass
int _length; // length of vtable (number of entries) ...
}

属性的介绍如下:

(1)_klass:该vtable所属的klass

(2)_tableOffset:vtable在klass实例内存中的偏移量

(3)_length:vtable的长度,即vtableEntry的数量,因为一个vtableEntry实例只包含一个Method*,其大小等于字宽(一个指针的宽度),所以vtable的长度跟vtable以字宽为单位的内存大小相同

下面介绍一下vtableEntry类。

vtableEntry类的定义及属性的声明如下:

class vtableEntry VALUE_OBJ_CLASS_SPEC {
...
private:
Method* _method;
...
};

这个类只是对_method进行了简单的封装。 

vtable表示是由一组变长(前面会有一个字段描述该表的长度)连续的vtableEntry元素构成的数组。其中每个vtableEntry封装了一个Method对象。在类初始化时,HotSpot将复制父类的vtable,然后根据自己定义的方法更新vtableEntry,或向vtable中添加新的vtableEntry对象。当Java方法重写父类方法时,HotSpot将更新vtable中表示被重写方法的vtableEntry,使其指向覆盖后的实现方法;如果是方法重载或者自身新增的方法,HotSpot将按顺序添加到vtable中。尚未提供实现的Java方法也放在了vtable中,因为没有实现,HotSpot没有为这个vtableEntry项分发具体的方法,这和C++的纯虚函数类似,不再赘述。调用类方法时,HotSpot通过ConstantPoolCacheEntry的_f2成员获取vtable中方法的索引,从而取到Method对象以便执行。关于ConstantPoolCacheEntry类及相关属性在后面会详细介绍。

2、klassItable

Java itable是Java接口函数表,为了方便查找某个接口对应的方法实现。itable的结构比vtable复杂,除了记录方法地址外还得记录该方法所属的接口类klass。

itable表由偏移表和方法表两个表组成,这两个表都是变长的。每个offset table entry保存的是类实现的一个接口klassOop和该接口方法表所在的偏移位置;方法表method table entry元素保存的是实现的接口方法,方法在方法表的位置同样是使用ConstantPoolCacheEntry的_f2成员保存的。在初始化itable时,HotSpot将类实现的接口以及实现的方法填写在上述两张表中。接口中的非public方法和abstract方法(在vtable中占一个槽位)不放入itable中。调用接口方法时,HotSpot通过ConstantPoolCacheEntry的_f1成员拿到接口的klassOop,在itable的偏移表中逐一匹配,如果匹配上则获取它的方法表的位置,然后在方法表中通过ConstantPoolCacheEntry的_f2成员找到实现的方法Method。
类及属性的定义如下:

class klassItable : public ResourceObj {
private:
instanceKlassHandle _klass; // my klass
int _table_offset; // offset of start of itable data within klass (in words)
int _size_offset_table; // size of offset table (in itableOffset entries)
int _size_method_table; // size of methodtable (in itableMethodEntry entries)
...
}

该类包含4个属性:

(1)_klass:itable所属的Klass

(2)_table_offset:itable在所属Klass中的内存偏移量

(3)_size_offset_table:itable中itableOffsetEntry的数量

(4)_size_method_table:itable中itableMethodEntry的数量

方法所属的接口类klass地址用itableOffsetEntry表示,类的定义如下:

class itableOffsetEntry VALUE_OBJ_CLASS_SPEC {
private:
Klass* _interface;
int _offset;
...
}

包含两个属性,如下:

(1) _interface:该方法所属的接口

(2)_offset:该接口下的第一个方法itableMethodEntry相对于所属Klass的偏移量

方法地址用itableMethodEntry表示,定义如下:

class itableMethodEntry VALUE_OBJ_CLASS_SPEC {
private:
Method* _method;
...
}

跟vtableEntry一样,只包含了一个_method属性。

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

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

相关文章的链接如下:

1、在Ubuntu 16.04上编译OpenJDK8的源代码

2、调试HotSpot源代码

3、HotSpot项目结构 

4、HotSpot的启动过程

5、HotSpot二分模型(1)

6、HotSpot的类模型(2)

7、HotSpot的类模型(3)

8、HotSpot的类模型(4)

9、HotSpot的对象模型(5)

10、HotSpot的对象模型(6)

11、操作句柄Handle(7)

12、句柄Handle的释放(8)

13、类加载器

14、类的双亲委派机制

15、核心类的预装载

16、Java主类的装载

17、触发类的装载

18、类文件介绍

19、文件流

20、解析Class文件

21、常量池解析(1)

22、常量池解析(2)

23、字段解析(1)

24、字段解析之伪共享(2)

25、字段解析(3)

26、字段解析之OopMapBlock(4)

27、方法解析之Method与ConstMethod介绍

28、方法解析

作者持续维护的个人博客classloading.com

关注公众号,有HotSpot源码剖析系列文章!

 

参考文章:

(1)C++与Java的多态性实现分析

(2)JVM Anatomy Park #16: 超多态虚调用  https://www.jianshu.com/p/704fce44840f

(3)The Black Magic of (Java) Method Dispatch  https://shipilev.net/blog/2015/black-magic-method-dispatch/

(4)https://www.zhihu.com/question/34846173?sort=created

(5)https://www.zhihu.com/question/56936880/answer/152203730

(6)https://hllvm-group.iteye.com/group/topic/29140

klassVtable与klassItable的更多相关文章

  1. 计算vtable的大小

    在ClassFileParser::parseClassFile()函数中会计算vtable和itable所需要的大小,因为vtable和itable是内嵌在Klass中的,parseClassFil ...

  2. 计算itable的大小

    在ClassFileParser::parseClassFile()函数中计算vtable和itable所需要的大小,之前已经介绍过vtable大小的计算,这一篇将详细介绍itable大小的计算过程. ...

  3. 解析Class文件之创建InstanceKlass对象

    ClassFileParser::parseClassFile()方法会将解析Class文件的大部分结果保存到instanceKlass对象中.创建instanceKlass对象的代码如下: int ...

  4. 初始化vtable

    在InstanceKlass::link_class_impl()方法中完成方法连接后会继续初始化vtable与itable,之前已经介绍过vtable与itable,并且在类解析过程中已经完成了大小 ...

  5. 初始化itable

    在InstanceKlass::link_class_impl()方法中完成方法连接后会继续初始化vtable与itable,之前已经介绍过vtable与itable,并且在类解析过程中已经完成了大小 ...

  6. HotSpot的执行引擎-CallStub栈帧

    之前多次提到接触到调用JavaCalls::call()方法来执行Java方法,如: (1)Java主类装载时,调用JavaCalls::call()方法执行的Java方法checkAndLoadMa ...

  7. Java引用类型之软引用(1)

    Java使用SoftReference来表示软引用,软引用是用来描述一些“还有用但是非必须”的对象.对于软引用关联着的对象,在JVM应用即将发生内存溢出异常之前,将会把这些软引用关联的对象列进去回收对 ...

  8. Java引用类型之软引用(2)

    下面接着上一篇介绍第2阶段和第3阶段的处理逻辑. 2.process_phase2() 第2个阶段移除所有的referent还存活的Reference,也就是从refs_list中移除Referenc ...

  9. Java引用类型之弱引用与幻像引用

    这一篇将介绍弱引用和幻像引用. 1.WeakReference WeakReference也就是弱引用,弱引用和软引用类似,它是用来描述"非必须"的对象的,它的强度比软引用要更弱一 ...

随机推荐

  1. Shaderlab-10chapter-立方体纹理、玻璃效果

    10.1.1天空盒子 window - Lighting - skyMaterial 创建mat,shader选自带的6 side shader 确保相机选skybox 如果某个相机需要覆盖,添加sk ...

  2. 记一次开发CefSharp做浏览器时Facebook广告页支付方式绑定不上Paypal问题

    问题:用CefSharp做浏览器开发.在做Facebook广告页面绑定Paypal支付方式时出现了绑定不上的问题. 让我们来还原问题的步骤: 第一步登录Facebook. 第二步进入广告绑卡页面选择P ...

  3. 大厂程序员教你如何学习C++(内附学习资料)

    目前准备面试同学都知道,C++是百度和腾讯的主流开发语言,而java是阿里的主流开发语言. 对于初学者来说,也不用纠结究竟学习c++还是java 其实只要好好掌握好一门即可,另一门即可融会贯通 因为我 ...

  4. three.js 将图片马赛克化

    这篇郭先生来说说BufferGeometry,类型化数组和粒子系统的使用,并且让图片有马赛克效果(同理可以让不清晰的图片清晰化),如图所示.在线案例点击博客原文 1. 解析图片 解析图片和上一篇一样 ...

  5. 超简单的jq图片上传

    <label class="file_img" for="file_imgs"> <input class="file_imgs&q ...

  6. C# POST请求中raw 参数的传递

    public static string PostmanPost() { var client = new RestClient("http://119.3.248.64:3000" ...

  7. placeholder CSS设置

    IE似乎一个冒号才生效,而chrome则是两个冒号才生效 input::-webkit-input-placeholder{ color:red; } input:-ms-input-placehol ...

  8. List<Activity> lists的关闭finish()

    public class App extends Application { private static List<Activity> lists = new ArrayList< ...

  9. jmeter单接口和多接口测试

    @@@@@@@@@@@@@@@ # 路漫漫其修远 最近接触到了多接口串联,接口串联的技术会在其他帖子有说明,其核心技术点就是通过正则表达式和变量来实现接口的关联.目前为止呢笔者用到的地方还只有一个,就 ...

  10. Vue中数组元素被替换,页面没有动态展示

    原始代码 页面没有相应goodsList替换,打印goodsList数据已经被替换: (借用https://www.cnblogs.com/belongs-to-qinghua/p/11112613. ...