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也就是弱引用,弱引用和软引用类似,它是用来描述"非必须"的对象的,它的强度比软引用要更弱一 ...
随机推荐
- 收集TCP端口的访问延迟和丢包率
需求: 找一款工具可以对TCP 80端口 收集 访问延迟和丢包率 找到的工具: 1.Hping : http://www.hping.org/ 2.paping : https://docs.azu ...
- (3)html-webpack-plugin的作用
在内存中生成index.html页面 在前面的内容中我们已经知道了如何在内存中打包main.js并引入到页面中. 同样的,我们也可以把index.html也打包放入到内存中. 安装html-webpa ...
- Java8之Stream 集合聚合操作集锦(含日常练习Demo)
Stream 是用函数式编程方式在集合类上进行复杂操作的工具,其集成了Java 8中的众多新特性之一的聚合操作,开发者可以更容易地使用Lambda表达式,并且更方便地实现对集合的查找.遍历.过滤以及常 ...
- 30页软件测试人面试宝典文档资料,助你拿下了百度、美团、字节跳动、小米等大厂的offer【内含答案】
前言:看了一下桌边的日历,新的6月,已经过去5天了.明天又是周六了,大家准备怎么度过呢?趁着大家周末给大家分享一个软件测试工程师面试题汇总. 拿到大厂的offer一直是软件测试朋友的一个目标,我是如何 ...
- CUDA C++ Extensions
敲代码的时候总是会去CUDA官方文档中找找思路,感觉每次看英文文档都要耗费一点时间来翻译,干脆自己翻译一下便于以后查阅.官方文档:cuda-c-language-extensions 目录 函数修饰符 ...
- paypal支付平台如何使用二次验证码_虚拟MFA_两步验证_谷歌身份验证器?
一般点账户名——设置——安全设置中开通虚拟MFA两步验证 具体步骤见链接 paypal支付平台如何使用二次验证码_虚拟MFA_两步验证_谷歌身份验证器? 二次验证码小程序于谷歌身份验证器APP的优势 ...
- 轻量级分布式延时任务处理组件easyTask-L-入门篇
今天给大家介绍一款新武器.我自研的一个java组件easyTask-L.这个是做啥的呢?我之前研发了一款单机版本的easyTask,这次是要介绍另外一款easyTask-L.区别就是后者支持分布式环境 ...
- jmeter接口测试 -- 数据库操作(mysql)
一.操作类型 语句类型 1.查询语句 2.非查询语句 1)update 2)insert into 3)删除 二.把返回值的化为变量 1.执行语句,并引用变量 2.查看结果
- ELasticSearch(五)ES集群原理与搭建
一.ES集群原理 查看集群健康状况:URL+ /GET _cat/health (1).ES基本概念名词 Cluster 代表一个集群,集群中有多个节点,其中有一个为主节点,这个主节点是可以通过选举产 ...
- Python编程无师自通PDF高清完整版免费下载|百度网盘
百度网盘:Python编程无师自通PDF高清完整版免费下载 提取码:cx73 内容介绍 畅销Python编程类入门书,美国亚马逊Kindle编程类排行榜榜一. 作者从文科毕业,通过自学编程转行为专业程 ...