专注虚拟机与编译器研究

第7.1篇-类的连接

在类的连接之前要保证对类进行了解析,例如初始化一个类时会调用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)Java对象分配原理

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

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

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

 

posted on 2020-08-11 08:39  鸠摩(马智)  阅读(849)  评论(0编辑  收藏  举报

导航