初始化itable
在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的源代码
13、类加载器
14、类的双亲委派机制
15、核心类的预装载
16、Java主类的装载
17、触发类的装载
18、类文件介绍
19、文件流
20、解析Class文件
21、常量池解析(1)
22、常量池解析(2)
23、字段解析(1)
24、字段解析之伪共享(2)
25、字段解析(3)
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的更多相关文章
- 计算itable的大小
在ClassFileParser::parseClassFile()函数中计算vtable和itable所需要的大小,之前已经介绍过vtable大小的计算,这一篇将详细介绍itable大小的计算过程. ...
- klassVtable与klassItable
klassVtable与klassItable类用来实现Java方法的多态,也可以叫动态绑定,是指在应用执行期间通过判断接受对象的实际类型,根据实际类型调用对应的方法.C++为了实现多态,在对象中嵌入 ...
- 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也就是弱引用,弱引用和软引用类似,它是用来描述"非必须"的对象的,它的强度比软引用要更弱一 ...
- Java引用类型之最终引用
FinalReference类只有一个子类Finalizer,并且Finalizer由关键字final修饰,所以无法继承扩展.类的定义如下: class FinalReference<T> ...
- JVM的方法执行引擎-entry point栈帧
接着上一篇去讲,回到JavaCalls::call_helper()中: address entry_point = method->from_interpreted_entry(); entr ...
- JVM的方法执行引擎-模板表
Java的模板解析执行需要模板表与转发表的支持,而这2个表中的数据在HotSpot虚拟机启动时就会初始化.这一篇首先介绍模板表. 在启动虚拟机阶段会调用init_globals()方法初始化全局模块, ...
随机推荐
- Spring RestTemplate 的介绍和使用-入门
RestTemplate是什么? 传统情况下在java代码里访问restful服务,一般使用Apache的HttpClient.不过此种方法使用起来太过繁琐.spring提供了一种简单便捷的模板类来进 ...
- TestNg失败重跑—解决使用 dataProvider 参数化用例次数冲突问题
问题背景 在使用 testng 执行 UI 自动化用例时,由于 UI自动化的不稳定性,我们在测试的时候,往往会加上失败重跑机制.在不使用 @DataProvider 提供用例参数化时,是不会有什么问题 ...
- day12:闭包函数&匿名函数
闭包函数 闭包函数的定义: 如果内函数使用了外函数的局部变量并且外函数把内函数返回出来的过程 叫做闭包里面的内函数是闭包函数 一个简单的闭包函数示例: def songyunjie_family(): ...
- JS内存机制
在看JS内存机制之前我们先来看一下JS是门什么样的语言,他又有哪些变量类型. 动静态,强弱类型 静态:在使用之前就需要确认其变量数据类型. 动态:在运行过程中需要检查数据类型. 强类型:不支持隐式类型 ...
- Puppeteer爬虫实战(三)
本篇文章针对大家熟知的技术站点作为目标进行技术实践. 确定需求 访问目标网站并按照筛选条件(关键词.日期.作者)进行检索并获取返回数据中的目标数据.进行技术拆分如下: 打开目标网站 找到输入框元素 ...
- web自动化 -- 框架
一.框架源码 https://github.com/jiangnan27/Autotest_UI_Open 二.框架大概介绍 Python3 + selenium3 + pytest5.3 + a ...
- web自动化 -- js操作(滑动屏幕、修改页面)
一.selenium对 js 的操作方法 1.先定义 js 操作 或者 定义 目标元素 2.执行 js 操作: driver.execute_script(js操作) 或者 ...
- 3分钟看懂Python后端必须知道的Django的信号机制!
概念 django自带一套信号机制来帮助我们在框架的不同位置之间传递信息.也就是说,当某一事件发生时,信号系统可以允许一个或多个发送者(senders)将通知或信号(signals)发送给一组接受者( ...
- Django学习路3
1.打开 Data Source alt insert 打开 Data Source 找到 db.sqlite3 确定 Download 下载后 TestConnection 测试是否成功 2.项目下 ...
- Python os.closerange() 方法
概述 os.closerange() 方法用于关闭所有文件描述符 fd,从 fd_low (包含) 到 fd_high (不包含), 错误会忽略.高佣联盟 www.cgewang.com 语法 clo ...