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. How to use the function of bind

    The usage of  bind  is to define a specified scope for called function. Because the key this is easy ...

  2. css 实现动态二级菜单

    动态实现简单的二级菜单 当鼠标放到一级标签上时,鼠标会变成小手的形状 展示二级菜单,源码如下,复制即可直接使用 <!DOCTYPE html> <html lang="en ...

  3. ArcGIS 10.2安装及卸载教程

    卸载 在控制面板中找到程序->卸载程序 找到ArcGIS的相关软件 这里以ArcGIS 10.2 Destop为例,选中,然后点击卸载,会出现如下界面 选择Remove,然后根据提示进行操作,即 ...

  4. Idea 自定义快捷代码输入 如syso => System.out.println()

    前言 之前一直用的Eclipse System.out.println()的快捷代码输入 是 syso,但是在Idea 不好使用了,后来搜索了一番才知道,在Idea中的快捷输入是 sout,这里我就想 ...

  5. db2创建nickname

    db2创建nickname创建步骤 1.创建 server create server servername type DB2/AIX version 10.5 wrapper drda authid ...

  6. SQL之DDL、DML、DCL、TCL

    SQL SQL(structured query language)是一种领域特定语言(DSL,domain-specific language),用于管理关系型数据库(relational data ...

  7. MySQL主从分离实现

    前言   大型网站为了减轻服务器处理海量的并发访问,所产生的性能问题,采用了很多解决方案,其中最主流的解决方案就是读写分离,即将读操作和写操作分别导流到不同的服务器集群执行,到了数据业务层,数据访问层 ...

  8. Redis网络模型的源码分析

    Redis的网络模型是基于I/O多路复用程序来实现的.源码中包含四种多路复用函数库epoll.select.evport.kqueue.在程序编译时会根据系统自动选择这四种库其中之一.下面以epoll ...

  9. 【laravel】用户认证之----手动认证用户

    模型 如果某个模型类需要用于认证,必须继承自 Illuminate\Foundation\Auth\User 基类,否则会报错.然后在这个模型类中使用 Notifiable Trait,里面提供了用户 ...

  10. Redis之NoSql入门和概述(一)

    1. 为什么用 NoSQL? NoSQL指的是非关系型的数据库,NoSQL也称作Not Only SQL的缩写,是对不同于传统的关系型数据库的数据库管理系统的统称.NoSQL用于超大规模数据的存储.这 ...