zoukankan      html  css  js  c++  java
  • 类的连接

    在类的连接之前要保证对类进行了解析,例如初始化一个类时会调用initialize_class()方法,实现如下:

    static void initialize_class(Symbol* class_name, TRAPS) {
      Klass* klass = SystemDictionary::resolve_or_fail(class_name, true, CHECK);
      InstanceKlass::cast(klass)->initialize(CHECK);
    }
    

    在类的初始化过程中,首先要调用SystemDictionary::resolve_or_fail()方法保证类被正确装载,如果类没有被装载,那么最终会调用到ClassFileParser::parseClassFile()方法装载类,并通过创建ConstantPool、Method、InstanceKlass等对象将元数据保存到HotSpot中。然后在调用的initialize()方法中会间接调用InstanceKlass::link_class_impl()方法进行类的连接。InstanceKlass::link_class_impl()方法的实现如下:

    bool InstanceKlass::link_class_impl(instanceKlassHandle this_oop, bool throw_verifyerror, TRAPS) {
    
      // return if already verified
      //  通过_init_state属性的值来判断类是否已经验证过
      if (this_oop->is_linked()) { 
        return true;
      }
    
      JavaThread* jt = (JavaThread*)THREAD;
    
      // 在连接子类之前先连接父类
      // link super class before linking this class
      instanceKlassHandle  super(THREAD, this_oop->super());
      if (super.not_null()) {
        if (super->is_interface()) {  // check if super class is an interface
          return false;
        }
    
        link_class_impl(super, throw_verifyerror, CHECK_false); // 递归调用此方法进行连接操作
      }
    
      // 连接该类实现的所有接口
      // link all interfaces implemented by this class before linking this class
      Array<Klass*>* interfaces = this_oop->local_interfaces();
      int num_interfaces = interfaces->length();
      for (int index = 0; index < num_interfaces; index++) {
        HandleMark hm(THREAD);
        instanceKlassHandle ih(THREAD, interfaces->at(index));
        link_class_impl(ih, throw_verifyerror, CHECK_false); // 递归调用此方法进行连接操作
      }
    
      // in case万一 the class is linked in the process of linking its superclasses
      if (this_oop->is_linked()) {
        return true;
      }
    
    
      // verification & rewriting 验证和重写
      {
        oop           init_lock = this_oop->init_lock();
        ObjectLocker  ol(init_lock, THREAD, init_lock != NULL);
        // rewritten will have been set if loader constraint error found
        // on an earlier link attempt
        // don't verify or rewrite if already rewritten
    
        if (!this_oop->is_linked()) {
          if (!this_oop->is_rewritten()) {
            {
              // 进行字节码验证
              bool verify_ok = verify_code(this_oop, throw_verifyerror, THREAD);
              if (!verify_ok) {
                 return false;
              }
            }
            // Just in case a side-effect of verify linked this class already
            // (which can sometimes happen since the verifier loads classes
            // using custom class loaders, which are free to initialize things)
            // 可能有时候会在验证的过程中导致类已经被连接,不过并不会进行类的初始化
            if (this_oop->is_linked()) { 
               return true;
            }
            // 重写类
            // also sets rewritten
            this_oop->rewrite_class(CHECK_false);
          } // end rewritten
    
          // 完成类的重写后进行方法连接
          // relocate jsrs and link methods after they are all rewritten
          this_oop->link_methods(CHECK_false);
    
          // Initialize the vtable and interface table after
          // methods have been rewritten since rewrite may fabricate(编造; 捏造) new Method*s.
          // also does loader constraint checking
          // 初始化vtable和itable
          if (!this_oop()->is_shared()) {
             ResourceMark rm(THREAD);
             klassVtable* kv = this_oop->vtable();
             kv->initialize_vtable(true, CHECK_false);
    
             klassItable* ki = this_oop->itable();
             ki->initialize_itable(true, CHECK_false);
          }
    
          // 将类的状态标记为已连接状态
          this_oop->set_init_state(linked);
        }// end linked
    
      }// end  verification & rewriting
    
      return true;
    }
    

    方法的逻辑非常清晰,我们可以很容易读懂类的连接过程,步骤总结如下:

    1. 连接父类和实现的接口。因为根据虚拟机规范,子类的初始化必然会导致父类的初始化,所以子类在连接之前自然要保证父类已经连接;
    2. 进行字节码验证;
    3. 重写类;
    4. 连接方法;
    5. 初始化vtable和itable;
    6. 将类的状态标记为已连接。

    大概的执行逻辑如下图所示。

    在连接类之前需要判断此类是否已经连接,之前介绍过,每个InstanceKlass中都定义了一个_init_state属性,如下:

    u1  _init_state;    // state of class
    

    值只能为ClassState枚举类中定义的枚举常量,如下:

    // See "The Java Virtual Machine Specification" section 2.16.2-5 for a detailed description
    // of the class loading & initialization procedure, and the use of the states.
    enum ClassState {
        allocated,            // allocated (but not yet linked)
        loaded,               // loaded and inserted in class hierarchy (but not linked yet)
        linked,               // successfully linked/verified (but not initialized yet)  连接包括验证、准备和解析阶段
        being_initialized,    // currently running class initializer
        fully_initialized,    // initialized (successfull final state)
        initialization_error  // error happened during initialization
    };
    

    这些状态主要标注一个类的加载、连接和初始化状态,3个过程已经在虚拟机规范中有了明确的规定,参考地址为https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html

    allocated状态表示已经分配内存,在InstanceKlass的构造函数中通常会将_init_state初始化为这个状态。

    loaded状态表示类已经装载并且已经插入到继承体系中,在 SystemDictionary::add_to_hierarchy()方法中会更新InstanceKlass的_init_state状态。

    linked状态表示已经成功连接/校验,只在InstanceKlass::link_class_impl()方法中更新为这个状态。

    另外3个状态是在类的初始化方法InstanceKlass::initialize_impl()中会使用,后面也会介绍到。

    相关文章的链接如下:

    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、字段解析之字段注入 

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

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

       

    参考文章:

    (1)Java对象分配原理

    (2)java符号的定位与解析(案例+源码解析)

    (3)方法的虚函数表(vtable与itable)

    (4)C++对象的内存布局(有对虚函数和虚函数表的介绍)   

  • 相关阅读:
    C++模板&泛型编程
    C++继承
    测试pc大、小端
    C语言标准定义的32个关键字
    *塔,菱形
    练习小题目
    c一些关键字
    uvaoj 489
    uvaoj1339
    hdu1969Pie(根据体积二分,分馅饼)
  • 原文地址:https://www.cnblogs.com/mazhimazhi/p/13470515.html
Copyright © 2011-2022 走看看