zoukankan      html  css  js  c++  java
  • 类的初始化

    对类进行初始化时,通常会调用如下方法:

    void InstanceKlass::initialize(TRAPS) {
      if (this->should_be_initialized()) {
        HandleMark hm(THREAD);
        instanceKlassHandle this_oop(THREAD, this);
        initialize_impl(this_oop, CHECK);
        // Note: at this point the class may be initialized
        //       OR it may be in the state of being initialized
        //       in case of recursive initialization!
      } else {
        assert(is_initialized(), "sanity check");
      }
    }
    

    调用InstanceKlass::initialize_impl()方法对类进行初始化。这个方法的源代码位置在oops/instanceKlass.cpp。在对类进行初始化之前必须保证类已经完成连接,I在连接完成后要进行类的初始化。initialize_impl()方法的实现如下:

    void InstanceKlass::initialize_impl(instanceKlassHandle this_oop, TRAPS) {
      // Make sure klass is linked (verified) before initialization
      // A class could already be verified, since it has been reflected upon.
      this_oop->link_class(CHECK);
    
      bool wait = false;
    
      // refer to the JVM book page 47 for description of steps
      // step 1:通过ObjectLocker在初始化之前进行加锁,防止多个线程并发初始化。
      {
        oop init_lock = this_oop->init_lock();
        ObjectLocker ol(init_lock, THREAD, init_lock != NULL);
    
        Thread *self = THREAD; // it's passed the current thread
    
        // step 2:如果当前instanceKlassHandle正在被初始化,且初始化线程不是当前线程,
        // 则执行ol.waitUninterruptibly(CHECK),等待其他线程初始化完成后通知。
        // If we were to use wait() instead of waitInterruptibly() then
        // we might end up throwing IE from link/symbol resolution sites
        // that aren't expected to throw.  This would wreak havoc.  See 6320309.
        while(
     	this_oop->is_being_initialized() &&          // 类正在进行初始化(being_initialized状态)
    	!this_oop->is_reentrant_initialization(self) // 执行初始化的线程不是当前的线程
        ){
            wait = true;
            ol.waitUninterruptibly(CHECK);
        }
    
        // step 3:当前类正在被当前线程正在被初始化。例如如果X类有静态变量指向new Y类实例,Y类中又有静态变量指向new X类实例,
        // 这样外部在调用X时需要初始化X类,初始化过程中又要触发Y类的初始化,而Y类初始化又再次触发X类的初始化
        if (
            this_oop->is_being_initialized() &&         // 类正在进行初始化(being_initialized状态)
    	this_oop->is_reentrant_initialization(self) // 执行初始化的线程的就是当前线程
        ){
           return;
        }
    
        // step 4:类已经初始化完成(fully_initialized状态)
        if (this_oop->is_initialized()) {
           return;
        }
    
        // step 5:类的初始化出错(initialization_error状态),则抛出NoClassDefFoundError异常
        if (this_oop->is_in_error_state()) {
          ResourceMark rm(THREAD);
          const char*  desc = "Could not initialize class ";
          const char*  className = this_oop->external_name();
          size_t       msglen = strlen(desc) + strlen(className) + 1;
          char*        message = NEW_RESOURCE_ARRAY(char, msglen);
          if (NULL == message) {
              // Out of memory: can't create detailed error message
              THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), className);
          } else {
              jio_snprintf(message, msglen, "%s%s", desc, className);
              THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), message);
          }
        }
    
        // step 6:设置类的初始化状态为being_initialized,设置初始化的线程为当前线程
        this_oop->set_init_state(being_initialized);
        this_oop->set_init_thread(self);
      }
    
      // step 7:如果当前初始化的不是接口、父类不为空并且父类未初始化,则初始化其父类
      Klass* super_klass = this_oop->super();
      if (
    	  super_klass != NULL &&
    	  !this_oop->is_interface() &&
    	  // 也就是判断super_klass的状态是否为fully_initialized,
    	  // 如果是,should_be_initialized()方法将返回true
    	  super_klass->should_be_initialized()
      ){
        super_klass->initialize(THREAD);
        // ...
      }
    
      if (this_oop->has_default_methods()) {
        // Step 7.5: 初始化有默认方法的接口
        for (int i = 0; i < this_oop->local_interfaces()->length(); ++i) {
          Klass*          iface = this_oop->local_interfaces()->at(i);
          InstanceKlass*  ik = InstanceKlass::cast(iface);
          if (ik->has_default_methods() && ik->should_be_initialized()) {
              ik->initialize(THREAD);
              // ...
          }
        }
      }
    
      // Step 8
      // 执行类或接口的初始化方法<clinit>
      {
        this_oop->call_class_initializer(THREAD); // 调用类或接口的<clinit>方法
      }
    
      // Step 9
      // 如果初始化过程没有异常,说明已经完成初始化,设置类的状态为full_initialized并通知其他线程初始化已经完成。
      if (!HAS_PENDING_EXCEPTION) {
        this_oop->set_initialization_state_and_notify(fully_initialized, CHECK);
      }
      else {
        // Step 10 and 11
        // 如果初始化过程发生异常,则通过set_initialization_state_and_notify()方法设置类的
        // 状态为initialization_error并通知其他线程,然后抛出错误或异常
        Handle e(THREAD, PENDING_EXCEPTION);
        CLEAR_PENDING_EXCEPTION;
        {
          EXCEPTION_MARK;
          this_oop->set_initialization_state_and_notify(initialization_error, THREAD);
          CLEAR_PENDING_EXCEPTION;   // ignore any exception thrown, class initialization error is thrown below
        }
        // ...
      }
    }
    

    方法执行的逻辑清晰的展示了类初始化所需要做的事情。在类初始化过程中会涉及到对类状态的判断,之前介绍过,使用InstanceKlass::_init_state来表示类的状态,取值如下:

    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
    };
    

    如果当前类正在被初始化,那么状态为being_initialized;如果当前类已经完成初始化,则状态为fully_initialized;如果当前类初始化出错,则状态为initialization_error。 

    在类初始化过程中,最重要的就是调用类的<clinit>方法了,大家如果不明白<clinit>方法的作用及产生的过程,可以参考本作者写的另外一本书《深入解析Java编译器:源码剖析与实例详解》,这本书将会详细介绍这个方法的来龙去脉。调用InstanceKlass::call_class_initializer()函数来执行<clinit>方法,函数的实现如下: 

    void InstanceKlass::call_class_initializer(TRAPS) {
      instanceKlassHandle ik (THREAD, this);
      call_class_initializer_impl(ik, THREAD);
    }
    
    void InstanceKlass::call_class_initializer_impl(instanceKlassHandle this_oop, TRAPS) {
      // ...
    
      methodHandle h_method(THREAD, this_oop->class_initializer());
      assert(!this_oop->is_initialized(), "we cannot initialize twice");
    
      if (h_method() != NULL) {
         JavaCallArguments  args; // No arguments
         JavaValue          result(T_VOID);
         JavaCalls::call(&result, h_method, &args, CHECK); // Static call (no args)
      }
    }
    

    最终通过调用JavaCalls::call()函数来完成Java方法的调用,这个函数的实现非常重要,在前面也多次接触过这个函数,不过目前还没有介绍相关的执行过程,在介绍方法执行引擎时会详细介绍。  

    相关文章的链接如下:

    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 

    40、初始化itable  

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

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

     

  • 相关阅读:
    SQL Server 2008通过PassPhrase加密数据
    SQL Server 2008之Values
    Merge(在一条语句中使用Insert,Update,Delete) 对两个表进行同步数据
    Linux yum常用命令介绍
    SQL Server 2008之WaitFor
    Android之TelephonyManager类的方法详解
    TextView里的文 html
    adb
    Apk得到Java源代码
    【Android】调用系统应用常用uri & intent设置
  • 原文地址:https://www.cnblogs.com/mazhimazhi/p/13495639.html
Copyright © 2011-2022 走看看