zoukankan      html  css  js  c++  java
  • 初始化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的源代码 

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

     

      

  • 相关阅读:
    索引有什么用?
    数据类型的优化策略?
    MySQL的锁策略有什么?
    行锁
    Innodb_lock_waits
    Innodb_locks表
    软件安装笔记
    Spring Boot学习笔记
    AI学习总结
    笔试面试题总结
  • 原文地址:https://www.cnblogs.com/mazhimazhi/p/13495636.html
Copyright © 2011-2022 走看看