在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源码剖析系列文章!