在InstanceKlass::link_class_impl()方法中完成方法连接后会继续初始化vtable与itable,之前已经介绍过vtable与itable,并且在类解析过程中已经完成了大小的计算并且也为相关信息的存储开辟了对应的内存空间,也就是在InstanceKlass本身需要占用的内存空间之后紧接着存储vtable,vtable后接着存储itable。这一篇将介绍itable的初始化。在InstanceKlass::link_class_impl()方法中的调用语句如下:

klassItable* ki = this_oop->itable();
ki->initialize_itable(true, CHECK_false);

调用itable()方法及相关调用链上的方法的实现如下:

klassItable* InstanceKlass::itable() const {
return new klassItable(instanceKlassHandle(this));
} klassItable::klassItable(instanceKlassHandle klass) {
_klass = klass; if (klass->itable_length() > 0) {
itableOffsetEntry* offset_entry = (itableOffsetEntry*)klass->start_of_itable();
if (offset_entry != NULL && offset_entry->interface_klass() != NULL) { // Check that itable is initialized
// First offset entry points to the first method_entry
intptr_t* method_entry = (intptr_t *)(((address)klass()) + offset_entry->offset());
intptr_t* end = klass->end_of_itable(); _table_offset = (intptr_t*)offset_entry - (intptr_t*)klass();
_size_offset_table = (method_entry - ((intptr_t*)offset_entry)) / itableOffsetEntry::size();
_size_method_table = (end - method_entry) / itableMethodEntry::size();
assert(_table_offset >= 0 && _size_offset_table >= 0 && _size_method_table >= 0, "wrong computation");
return;
}
} // The length of the itable was either zero, or it has not yet been initialized.
_table_offset = 0;
_size_offset_table = 0;
_size_method_table = 0;
}
intptr_t* start_of_itable() const {
return start_of_vtable() + align_object_offset(vtable_length());
} intptr_t* end_of_itable() const {
return start_of_itable() + itable_length();
}

如上各个属性的说明如下图所示。

构造函数中根据现有的信息初始化了klassItable中的各个属性,这几个属性在之前已经介绍过,如下:

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)
...
}

接下来在在InstanceKlass::link_class_impl()方法中调用klassItable::initialize_itable()方法对itable进行初始化,如下:

// Initialization
void klassItable::initialize_itable(bool checkconstraints, TRAPS) {
if (_klass->is_interface()) {
// This needs to go after vtable indices are assigned but
// before implementors need to know the number of itable indices.
assign_itable_indices_for_interface(_klass());
} // Cannot be setup doing bootstrapping, interfaces don't have
// itables, and klass with only ones entry have empty itables
if (
Universe::is_bootstrapping() ||
_klass->is_interface() ||
_klass->itable_length() == itableOffsetEntry::size()
){
return;
} // There's alway an extra itable entry so we can null-terminate it.
guarantee(size_offset_table() >= 1, "too small");
int num_interfaces = size_offset_table() - 1;
if (num_interfaces > 0) {
// Iterate through all interfaces
int i;
for(i = 0; i < num_interfaces; i++) {
itableOffsetEntry* ioe = offset_entry(i);
HandleMark hm(THREAD);
KlassHandle interf_h (THREAD, ioe->interface_klass());
assert(interf_h() != NULL && ioe->offset() != 0, "bad offset entry in itable");
initialize_itable_for_interface(ioe->offset(), interf_h, checkconstraints, CHECK);
} }
}

此方法调用的方法比较多,完成的逻辑也比较多,下面详细介绍。  

1、assign_itable_indices_for_interface()方法

如果当前处理的是接口,那么会调用klassItable::assign_itable_indices_for_interface()方法为接口中的方法指定itableEntry索引,方法的实现如下:

int klassItable::assign_itable_indices_for_interface(Klass* klass) {
// an interface does not have an itable, but its methods need to be numbered
Array<Method*>* methods = InstanceKlass::cast(klass)->methods();
int nof_methods = methods->length();
int ime_num = 0;
for (int i = 0; i < nof_methods; i++) {
Method* m = methods->at(i);
if (interface_method_needs_itable_index(m)) {
assert(!m->is_final_method(), "no final interface methods");
// If m is already assigned a vtable index, do not disturb it.
if (!m->has_vtable_index()) { // 当_vtable_index>=0时,表示指定了vtable index
assert(m->vtable_index() == Method::pending_itable_index, "set by initialize_vtable");
m->set_itable_index(ime_num);
// Progress to next itable entry
ime_num++;
}
}
}
assert(ime_num == method_count_for_interface(klass), "proper sizing");
return ime_num;
} inline bool interface_method_needs_itable_index(Method* m) {
if (m->is_static())
return false; // e.g., Stream.empty
if (m->is_initializer())
return false; // <init> or <clinit>
// If an interface redeclares a method from java.lang.Object,
// it should already have a vtable index, don't touch it.
// e.g., CharSequence.toString (from initialize_vtable)
// if (m->has_vtable_index()) return false; // NO!
return true;
}

对于需要itableEntry的方法来说,为其指定itable index。

2、initialize_itable_for_interface()方法

对于类实现的每个接口,调用klassItable::initialize_itable_for_interface()方法进行处理,如下:

void klassItable::initialize_itable_for_interface(int method_table_offset, KlassHandle interf_h, bool checkconstraints, TRAPS) {
Array<Method*>* methods = InstanceKlass::cast(interf_h())->methods();
int nof_methods = methods->length();
HandleMark hm;
assert(nof_methods > 0, "at least one method must exist for interface to be in vtable");
Handle interface_loader (THREAD, InstanceKlass::cast(interf_h())->class_loader()); int ime_count = method_count_for_interface(interf_h());
for (int i = 0; i < nof_methods; i++) {
Method* m = methods->at(i);
methodHandle target;
if (m->has_itable_index()) {
// This search must match the runtime resolution, i.e. selection search for invokeinterface
// to correctly enforce loader constraints for interface method inheritance
LinkResolver::lookup_instance_method_in_klasses(target, _klass, m->name(), m->signature(), CHECK);
}
if (target == NULL || !target->is_public() || target->is_abstract()) {
// Entry does not resolve. Leave it empty for AbstractMethodError.
if (!(target == NULL) && !target->is_public()) {
// Stuff an IllegalAccessError throwing method in there instead.
itableOffsetEntry::method_entry(_klass(), method_table_offset)[m->itable_index()].
initialize(Universe::throw_illegal_access_error());
}
} else {
// ime may have moved during GC so recalculate address
int ime_num = m->itable_index();
assert(ime_num < ime_count, "oob");
itableOffsetEntry::method_entry(_klass(), method_table_offset)[ime_num].initialize(target());
}
}
} int klassItable::method_count_for_interface(Klass* interf) {
assert(interf->oop_is_instance(), "must be");
assert(interf->is_interface(), "must be");
Array<Method*>* methods = InstanceKlass::cast(interf)->methods();
int nof_methods = methods->length();
while (nof_methods > 0) {
Method* m = methods->at(nof_methods-1);
if (m->has_itable_index()) {
int length = m->itable_index() + 1;
return length; // return the rightmost itable index, plus one
}
nof_methods -= 1;
}
// no methods have itable indices
return 0;
}

遍历接口中的每个方法,如果方法指定了itable index,调用 LinkResolver::lookup_instance_method_in_klasses()方法进行处理,这个方法的实现如下:

// returns first instance method
// Looks up method in classes, then looks up local default methods
void LinkResolver::lookup_instance_method_in_klasses(methodHandle& result, KlassHandle klass, Symbol* name, Symbol* signature, TRAPS) {
Method* result_oop = klass->uncached_lookup_method(name, signature);
result = methodHandle(THREAD, result_oop);
// 循环查找方法的接口实现
while (!result.is_null() && result->is_static() && result->method_holder()->super() != NULL) {
KlassHandle super_klass = KlassHandle(THREAD, result->method_holder()->super());
result = methodHandle(THREAD, super_klass->uncached_lookup_method(name, signature));
} // 当从拥有Itable的类或父类中找到接口中方法的实现时,result不为NULL,否则为NULL,这时候就要查找默认的方法了
if (result.is_null()) {
Array<Method*>* default_methods = InstanceKlass::cast(klass())->default_methods();
if (default_methods != NULL) {
result = methodHandle(InstanceKlass::find_method(default_methods, name, signature));
assert(result.is_null() || !result->is_static(), "static defaults not allowed");
}
}
}

在klassItable::initialize_itable_for_interface()方法中调用的initialize()方法的实现如下:  

// Initialize a itableMethodEntry
void itableMethodEntry::initialize(Method* m) {
if (m == NULL)
return;
_method = m;
}

初始化itableMethodEntry类中定义的唯一属性_method。

相关文章的链接如下:

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、方法解析

29、klassVtable与klassItable类的介绍

30、计算vtable的大小

31、计算itable的大小

32、解析Class文件之创建InstanceKlass对象

33、字段解析之字段注入

34、类的连接

35、类的连接之验证

36、类的连接之重写(1)

37、类的连接之重写(2)

38、方法的连接

39、初始化vtable

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

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

  

初始化itable的更多相关文章

  1. 计算itable的大小

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

  2. klassVtable与klassItable

    klassVtable与klassItable类用来实现Java方法的多态,也可以叫动态绑定,是指在应用执行期间通过判断接受对象的实际类型,根据实际类型调用对应的方法.C++为了实现多态,在对象中嵌入 ...

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

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

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

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

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

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

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

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

  7. Java引用类型之最终引用

    FinalReference类只有一个子类Finalizer,并且Finalizer由关键字final修饰,所以无法继承扩展.类的定义如下: class FinalReference<T> ...

  8. JVM的方法执行引擎-entry point栈帧

    接着上一篇去讲,回到JavaCalls::call_helper()中: address entry_point = method->from_interpreted_entry(); entr ...

  9. JVM的方法执行引擎-模板表

    Java的模板解析执行需要模板表与转发表的支持,而这2个表中的数据在HotSpot虚拟机启动时就会初始化.这一篇首先介绍模板表. 在启动虚拟机阶段会调用init_globals()方法初始化全局模块, ...

随机推荐

  1. NIO实践-HTTP交互实现暨简版Tomcat交互内核

    今天就NIO实现简单的HTTP交互做一下笔记,进而来加深Tomcat源码印象. 一.关于HTTP 1.HTTP的两个显著特点,HTTP是一种可靠的超文本传输协议 第一.实际中,浏览器作为客户端,每次访 ...

  2. python多线程之Threading

    什么是线程? 线程是操作系统内核调度的基本单位,一个进程中包含一个或多个线程,同一个进程内的多个线程资源共享,线程相比进程是“轻”量级的任务,内核进行调度时效率更高. 多线程有什么优势? 多线程可以实 ...

  3. JVM系列之:对象的锁状态和同步

    目录 简介 java对象头 java中锁状态的变化 偏向锁biased locking 轻量级锁thin lock 重量级锁 三种锁状态的不同 简介 锁和同步是java多线程编程中非常常见的使用场景. ...

  4. 【laravel】基于jwt实现用户认证

    安装及基础配置 使用 composer 安装 # 建议使用1.0以上版本 composer require tymon/jwt-auth .*@rc 进行一些配置 有些文档会说要添加 Tymon\JW ...

  5. dubbo泛化调用 小demo

    前两天刚好有个同事来问是否用过 dubbo泛化 调用,不需要通过指定配置.第一次听到的时候,还是有点懵,但觉得有意思,可以学点东西. 立马百度了,找了demo,这篇比较容易上手(http://www. ...

  6. matplotlib基础汇总_01

    灰度化处理就是将一幅色彩图像转化为灰度图像的过程.彩色图像分为R,G,B三个分量,分别显示出红绿蓝等各种颜色,灰度化就是使彩色的R,G,B分量相等的过程.灰度值大的像素点比较亮(像素值最大为255,为 ...

  7. PHP quoted_printable_encode() 函数

    定义和用法 quoted_printable_encode() 函数把 8 位字符串转换为 quoted-printable 字符串. 提示:经过 quoted-printable 编码后的数据与通过 ...

  8. Skill 扫描list中是否含有某元素

    https://www.cnblogs.com/yeungchie/ code procedure(ycInListp(scan keylist) prog((times) times = 0 for ...

  9. 关于ORACLE索引的几种扫描方式

    ------------恢复内容开始------------ ------------恢复内容开始------------ 一条sql执行的效率因执行计划的差异而影响,经常说这条sql走索引了,那条s ...

  10. Raft协议理解

    raft协议最关键的部分是领导选举和日志复制 日志复制 日志匹配原则:如果两个日志在相同索引位置的entry的任期号相同,那么这两个日志从头到这个索引位置之前完全相同. 日志匹配原则可以解释为如下两条 ...