zoukankan      html  css  js  c++  java
  • 核心类的预装载

    查看Universe::genesis()函数的实现,其中有对数组及核心类的加载逻辑。数组类没有对应的Class文件,所以在类装载阶段,基本类型的一维数组会被虚拟机直接创建,也不需要进行验证、准备和初始化等操作;类加载就是通过宏来定义一些需要加载的核心类,然后调用前面介绍的一些类加载器方法来加载类。基本类型的一维数组的创建如下:

    源代码位置:hotspot/src/share/vm/memory/universe.cpp
    
    void Universe::genesis(TRAPS) {
      ResourceMark rm;
    
      {
        {
          MutexLocker mc(Compile_lock);
          // determine base vtable size; without that we cannot create the array klasses
          compute_base_vtable_size();
    
          if (!UseSharedSpaces) {
            _boolArrayKlassObj      = TypeArrayKlass::create_klass(T_BOOLEAN, sizeof(jboolean), CHECK);
            _charArrayKlassObj      = TypeArrayKlass::create_klass(T_CHAR,    sizeof(jchar),    CHECK);
            _singleArrayKlassObj    = TypeArrayKlass::create_klass(T_FLOAT,   sizeof(jfloat),   CHECK);
            _doubleArrayKlassObj    = TypeArrayKlass::create_klass(T_DOUBLE,  sizeof(jdouble),  CHECK);
            _byteArrayKlassObj      = TypeArrayKlass::create_klass(T_BYTE,    sizeof(jbyte),    CHECK);
            _shortArrayKlassObj     = TypeArrayKlass::create_klass(T_SHORT,   sizeof(jshort),   CHECK);
            _intArrayKlassObj       = TypeArrayKlass::create_klass(T_INT,     sizeof(jint),     CHECK);
            _longArrayKlassObj      = TypeArrayKlass::create_klass(T_LONG,    sizeof(jlong),    CHECK);
    
            _typeArrayKlassObjs[T_BOOLEAN] = _boolArrayKlassObj;
            _typeArrayKlassObjs[T_CHAR]    = _charArrayKlassObj;
            _typeArrayKlassObjs[T_FLOAT]   = _singleArrayKlassObj;
            _typeArrayKlassObjs[T_DOUBLE]  = _doubleArrayKlassObj;
            _typeArrayKlassObjs[T_BYTE]    = _byteArrayKlassObj;
            _typeArrayKlassObjs[T_SHORT]   = _shortArrayKlassObj;
            _typeArrayKlassObjs[T_INT]     = _intArrayKlassObj;
            _typeArrayKlassObjs[T_LONG]    = _longArrayKlassObj;
    
            // ...
          }
        }
        // ...
    }

    基本类型的一维数组的创建在前面介绍类模型时介绍过,这里不再介绍。有了基本类型的一维数组后,可以方便地创建出任何维度的基本类型数组。

    对于引用类型数组,只要创建出表示引用类型的Klass对象,就可以根据Klass及ObjTypeKlass中的一些字段表示出引用类型数组,例如创建Object一维数组。在Universe::genesis()函数中同样有如下调用:

    InstanceKlass* ik = InstanceKlass::cast(SystemDictionary::Object_klass());
    _objectArrayKlassObj = ik->array_klass(1, CHECK);

    在之前已经介绍过,这里不再介绍。  

    HotSpot在启动过程中会预先加载一些核心类,如Object、String等,如下: 

    源代码位置:/src/share/vm/classfile/systemDictionary.hpp
    #define WK_KLASSES_DO(do_klass)                                 
    /* well-known classes */                                        
    do_klass(Object_klass,       java_lang_Object ,         Pre)    
    do_klass(String_klass,       java_lang_String,          Pre )   
    do_klass(Class_klass,        java_lang_Class,           Pre )   
    do_klass(Cloneable_klass,    java_lang_Cloneable,       Pre )   
    do_klass(ClassLoader_klass,  java_lang_ClassLoader,     Pre )   
    do_klass(Serializable_klass, java_io_Serializable,      Pre)    
    do_klass(System_klass,       java_lang_System,          Pre )   
    ...
    

    如上通过宏定义了一些类及类的一些相关属性,这个宏在枚举类WKID中使用,如下:

    enum WKID {
        NO_WKID = 0,
     
        #define WK_KLASS_ENUM(name, symbol, ignore_o) WK_KLASS_ENUM_NAME(name), WK_KLASS_ENUM_NAME(symbol) = WK_KLASS_ENUM_NAME(name),
        WK_KLASSES_DO(WK_KLASS_ENUM)
        #undef WK_KLASS_ENUM
     
        WKID_LIMIT,
        FIRST_WKID = NO_WKID + 1
    };
    

    经过宏扩展后,变为了如下的形式:

    enum WKID {
      NO_WKID = 0,
     
      Object_klass_knum,        java_lang_Object_knum = Object_klass_knum,           
      String_klass_knum,        java_lang_String_knum = String_klass_knum,           
      Class_klass_knum,         java_lang_Class_knum = Class_klass_knum,             
      Cloneable_klass_knum,     java_lang_Cloneable_knum = Cloneable_klass_knum,      
      ClassLoader_klass_knum,   java_lang_ClassLoader_knum = ClassLoader_klass_knum,  
      Serializable_klass_knum,  java_io_Serializable_knum = Serializable_klass_knum,  
      System_klass_knum,        java_lang_System_knum = System_klass_knum,            
      ...
     
      WKID_LIMIT,                    // 70
      FIRST_WKID = NO_WKID + 1       // 1
    };
    

    这些类在HotSpot启动时就会进行预加载,调用链路如下:

    Universe::genesis()                                universe.cpp
    SystemDictionary::initialize()                     systemDictionary.cpp
    SystemDictionary::initialize_preloaded_classes()   systemDictionary.cpp
    SystemDictionary::initialize_wk_klasses_through()  systemDictionary.hpp
    SystemDictionary::initialize_wk_klasses_until()    systemDictionary.cpp
    

    SystemDictionary::initialize_preloaded_classes()是分批次预加载类的。首先会调用SystemDictionary::initialize_wk_klasses_until()函数,在这个函数中遍历WK_KLASSES_DO宏中定义的所有需要预加载的类,函数的具体实现如下:

    void SystemDictionary::initialize_wk_klasses_until(WKID limit_id, WKID &start_id, TRAPS) {
      assert((int)start_id <= (int)limit_id, "IDs are out of order!");
      for (int id = (int)start_id; id < (int)limit_id; id++) {
        assert(id >= (int)FIRST_WKID && id < (int)WKID_LIMIT, "oob");
        int info = wk_init_info[id - FIRST_WKID];
        int sid  = (info >> CEIL_LG_OPTION_LIMIT);
        // right_n_bits的宏扩展为((CEIL_LG_OPTION_LIMIT >= BitsPerWord ? 0 : OneBit << (CEIL_LG_OPTION_LIMIT)) - 1)
        int opt  = (info & right_n_bits(CEIL_LG_OPTION_LIMIT));
     
        initialize_wk_klass((WKID)id, opt, CHECK);
      }
      // move the starting value forward to the limit:
      start_id = limit_id;
    }
    

    其中wk_init_info数组的定义如下:

    static const short wk_init_info[] = {
      #define WK_KLASS_INIT_INFO(name, symbol, option) 
        ( ((int)vmSymbols::VM_SYMBOL_ENUM_NAME(symbol) << SystemDictionary::CEIL_LG_OPTION_LIMIT) | (int)SystemDictionary::option ),
      WK_KLASSES_DO(WK_KLASS_INIT_INFO)
      #undef WK_KLASS_INIT_INFO
      0
    };
    

    最终的wk_init_info数组经过宏扩展后如下:

    static const short wk_init_info[] = {
    ( ((int)vmSymbols::java_lang_Object_enum << SystemDictionary::CEIL_LG_OPTION_LIMIT)            | (int)SystemDictionary::Pre ), 
    ( ((int)vmSymbols::java_lang_String_enum << SystemDictionary::CEIL_LG_OPTION_LIMIT)            | (int)SystemDictionary::Pre ), 
    ( ((int)vmSymbols::java_lang_Class_enum  << SystemDictionary::CEIL_LG_OPTION_LIMIT)            | (int)SystemDictionary::Pre ), 
    ( ((int)vmSymbols::java_lang_Cloneable_enum << SystemDictionary::CEIL_LG_OPTION_LIMIT)         | (int)SystemDictionary::Pre ), 
    ( ((int)vmSymbols::java_lang_ClassLoader_enum  << SystemDictionary::CEIL_LG_OPTION_LIMIT)      | (int)SystemDictionary::Pre ), 
    ( ((int)vmSymbols::java_io_Serializable_enum  << SystemDictionary::CEIL_LG_OPTION_LIMIT)       | (int)SystemDictionary::Pre ), 
    ( ((int)vmSymbols::java_lang_System_enum  << SystemDictionary::CEIL_LG_OPTION_LIMIT)           | (int)SystemDictionary::Pre ), 
    ...
    0
    };
    

    在SystemDictionary::initialize_wk_klasses_until()函数或如上wk_init_info数组中用到的CEIL_LG_OPTION_LIMIT是枚举变量,定义在InitOption枚举类中,如下:

    enum InitOption {
        Pre,                        // preloaded; error if not present
        Pre_JSR292,                 // preloaded if EnableInvokeDynamic
     
        // Order is significant.  Options before this point require resolve_or_fail.
        // Options after this point will use resolve_or_null instead.
     
        Opt,                        // preload tried; NULL if not present
        Opt_Only_JDK14NewRef,       // preload tried; use only with NewReflection
        Opt_Only_JDK15,             // preload tried; use only with JDK1.5+
        OPTION_LIMIT,
        CEIL_LG_OPTION_LIMIT = 4  // OPTION_LIMIT <= (1<<CEIL_LG_OPTION_LIMIT)  ceil_lg_option_limit
      };
    

    在initialize_wk_klasses_until()函数中调用的initialize_wk_klasses()函数的实现如下: 

    bool SystemDictionary::initialize_wk_klass(WKID id, int init_opt, TRAPS) {
      assert(id >= (int)FIRST_WKID && id < (int)WKID_LIMIT, "oob");
      int  info = wk_init_info[id - FIRST_WKID];
      int  sid  = (info >> CEIL_LG_OPTION_LIMIT);
      Symbol* symbol = vmSymbols::symbol_at((vmSymbols::SID)sid);
      Klass** klassp = &_well_known_klasses[id];
      bool must_load = (init_opt < SystemDictionary::Opt);
      if ((*klassp) == NULL) {
         if (must_load) {
            (*klassp) = resolve_or_fail(symbol, true, CHECK_0); // load required class
         } else {
            (*klassp) = resolve_or_null(symbol, CHECK_0); // load optional klass
         }
      }
      return ((*klassp) != NULL);
    }
    

    调用resolve_or_fail()或resolve_or_null()函数进行类的加载,由名称也可以区分出这两个方法的区别。resolve_or_fail()方法表示加载的类一定要成功加载,也就是在wk_init_info数组中标注有Pre与Pre_JSR292的类;resolve_or_null()方法表示加载的类如果有就加载,没有返回NULL即可。 

    最终会调用到SystemDictionary::load_instance_class()函数,如在加载核心类时,调用链路如下:

    SystemDictionary::resolve_or_fail()                   systemDictionary.cpp
    SystemDictionary::resolve_or_fail()                   systemDictionary.cpp
    SystemDictionary::resolve_or_null()                   systemDictionary.cpp
    SystemDictionary::resolve_instance_class_or_null()    systemDictionary.cpp
    SystemDictionary::load_instance_class()               systemDictionary.cpp
    

    resolve_or_fail()方法的实现如下: 

    Klass* SystemDictionary::resolve_or_fail(Symbol* class_name, Handle class_loader, Handle protection_domain, bool throw_error, TRAPS) {
      Klass* klass = resolve_or_null(class_name, class_loader, protection_domain, THREAD);
      if (HAS_PENDING_EXCEPTION || klass == NULL) { 
         KlassHandle k_h(THREAD, klass);
         // can return a null klass
         klass = handle_resolution_exception(class_name, class_loader, protection_domain, throw_error, k_h, THREAD);
      }
      return klass;
    }
    

    调用resolve_or_null()方法来加载类,不过klass一定不能为空,如果为空则出异常。调用的resolve_or_null()方法的实现如下:

    Klass* SystemDictionary::resolve_or_null(Symbol* class_name, Handle class_loader, Handle protection_domain, TRAPS) {
      // 数组,通过签名的格式来判断
      if (FieldType::is_array(class_name)) {
        return resolve_array_class_or_null(class_name, class_loader, protection_domain, CHECK_NULL);
      }
      // 普通类,通过签名的格式来判断
      else if (FieldType::is_obj(class_name)) {
        ResourceMark rm(THREAD);
        // Ignore wrapping L and ;.
        TempNewSymbol name = SymbolTable::new_symbol(class_name->as_C_string() + 1,
                                                     class_name->utf8_length() - 2,
    						 CHECK_NULL);
        return resolve_instance_class_or_null(name, class_loader, protection_domain, CHECK_NULL);
      }
      else {
        return resolve_instance_class_or_null(class_name, class_loader, protection_domain, CHECK_NULL);
      }
    }
    

    调用resolve_array_class_or_null()方法来加载数组,调用resolve_instance_class_or_null()方法加载类。数组类没有对应的Class文件,所以在类装载阶段,数组类会被虚拟机直接创建,并且数组类在装载完成后的状态为generated,即不需要进行验证、准备和初始化等操作。方法已经在前一篇介绍过,这里不再介绍。  

    相关文章的链接如下:

    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、类的双亲委派机制 

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

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

      

      

  • 相关阅读:
    JavaScript 基础第七天(DOM的开始)
    JavaScript 基础第六天
    JavaScript 基础第五天
    JavaScript 基础第四天
    JavaScript 基础第三天
    JavaScript 基础第二天
    观《幸福终点站》有感
    山东移动2014校园招聘笔试
    Genymotion虚拟Android不能联网的一种解决方法
    关于Thinkpad E420双显卡驱动安装和切换的问题
  • 原文地址:https://www.cnblogs.com/mazhimazhi/p/13343296.html
Copyright © 2011-2022 走看看