klassVtable与klassItable
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的源代码
13、类加载器
14、类的双亲委派机制
15、核心类的预装载
16、Java主类的装载
17、触发类的装载
18、类文件介绍
19、文件流
20、解析Class文件
21、常量池解析(1)
22、常量池解析(2)
23、字段解析(1)
24、字段解析之伪共享(2)
25、字段解析(3)
28、方法解析
作者持续维护的个人博客classloading.com。
关注公众号,有HotSpot源码剖析系列文章!
参考文章:
(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的更多相关文章
- 计算vtable的大小
在ClassFileParser::parseClassFile()函数中会计算vtable和itable所需要的大小,因为vtable和itable是内嵌在Klass中的,parseClassFil ...
- 计算itable的大小
在ClassFileParser::parseClassFile()函数中计算vtable和itable所需要的大小,之前已经介绍过vtable大小的计算,这一篇将详细介绍itable大小的计算过程. ...
- 解析Class文件之创建InstanceKlass对象
ClassFileParser::parseClassFile()方法会将解析Class文件的大部分结果保存到instanceKlass对象中.创建instanceKlass对象的代码如下: int ...
- 初始化vtable
在InstanceKlass::link_class_impl()方法中完成方法连接后会继续初始化vtable与itable,之前已经介绍过vtable与itable,并且在类解析过程中已经完成了大小 ...
- 初始化itable
在InstanceKlass::link_class_impl()方法中完成方法连接后会继续初始化vtable与itable,之前已经介绍过vtable与itable,并且在类解析过程中已经完成了大小 ...
- HotSpot的执行引擎-CallStub栈帧
之前多次提到接触到调用JavaCalls::call()方法来执行Java方法,如: (1)Java主类装载时,调用JavaCalls::call()方法执行的Java方法checkAndLoadMa ...
- Java引用类型之软引用(1)
Java使用SoftReference来表示软引用,软引用是用来描述一些“还有用但是非必须”的对象.对于软引用关联着的对象,在JVM应用即将发生内存溢出异常之前,将会把这些软引用关联的对象列进去回收对 ...
- Java引用类型之软引用(2)
下面接着上一篇介绍第2阶段和第3阶段的处理逻辑. 2.process_phase2() 第2个阶段移除所有的referent还存活的Reference,也就是从refs_list中移除Referenc ...
- Java引用类型之弱引用与幻像引用
这一篇将介绍弱引用和幻像引用. 1.WeakReference WeakReference也就是弱引用,弱引用和软引用类似,它是用来描述"非必须"的对象的,它的强度比软引用要更弱一 ...
随机推荐
- Python面向对象02/类的空间问题、类与对象之间的关系、类与类之间的关系
Python面向对象02/类的空间问题.类与对象之间的关系.类与类之间的关系 目录 Python面向对象02/类的空间问题.类与对象之间的关系.类与类之间的关系 1. 类的空间问题 2. 类与对象之间 ...
- Static关键字的使用
Static关键字的使用 static关键字的作用: 用来修饰成员变量和方法,被修饰的成员是属于类的,而不单单是属于某个对象的,也就是说,可以不靠对象来调用. 首先我们来介绍类变量 当static修饰 ...
- 基于python的自动化测试简介【十年从业大佬】
一.自动化测试包括以下几个方面: 1. 常用测试工具: (1)QTP:主要用于回归测试和测试同一软件的新版本 (2)Robot Framwork:python编写的功能自动化测试框架,具有良好的可扩展 ...
- SpringBoot 接收前端参数的几种方式
昨天和前端小伙伴在联调是碰到了参数接收不到的错误,我在postman上测试接口是正常的,但是与前端对接时就接受不到参数,请求方式都是get,但是问题就在于json 和 form-data 的区别!这 ...
- Makefile中的一个坑
问题描述:Makefile中,我想将一个变量的后缀全部进行替换,如将所有的.c后缀变成.d后缀 方法:$(CUR_SOURCE: .c = .d ) 说明:查阅相关资料,了解到上述这种语法就可以将所有 ...
- 深入掌握K8S Pod
k8s系列文章: 什么是K8S K8S configmap介绍 Pod是k8s中最小的调度单元,包含了一个"根容器"和其它用户业务容器. 如果你使用过k8s的话,当然会了解pod的 ...
- canvas学习01
canvas 必须指定宽高,确定可绘图区域的大小 canvas标签里写的是浏览器不支持canvas时展示的内容 <canvas id="drawing" width=&quo ...
- P3756 [CQOI2017]老C的方块
题目链接 看到网格图+最优化问题,当然要想黑白染色搞网络流.不过这道题显然无法用黑白染色搞定. 仔细观察那四种图形,发现都是蓝线两边一定有两个格子,两个格子旁边一定还有且仅有一个格子.因此我们可以这么 ...
- wpf文字模糊
wpf如果使用了DropShadowEffect,会导致文字模糊,可以在window上设置 this.UseLayoutRounding = true;解决此问题
- onsubmit校验表单时利用ajax的return false无效解决方法-转
原来的代码 function checkNewEmail(){ var re_email=new RegExp("\\w+@\\w+\\.\\w+\\.?\\w*"); ...