专注虚拟机与编译器研究

第3.3篇-核心类的预装载

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

1、核心类的加载

HotSpot VM在启动过程中会预加载一些核心类,如ObjectString等。需要预加载的类通过如下的宏进行定义:

源代码位置:openjdk/hotspot/src/share/vm/classfile/systemDictionary.hpp

#define WK_KLASSES_DO(do_klass)                        \
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中使用,如下:

源代码位置:openjdk/hotspot/src/share/vm/classfile/systemDictionary.hpp

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 VM启动时就会预加载,调用链路如下:

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宏中表示的所有需要预加载的类,函数的实现如下:

源代码位置:openjdk/hotspot/src/share/vm/classfile/systemDictionary.cpp

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);
  }
  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枚举类中,如下:

源代码位置:openjdk/hotspot/src/share/vm/classfile/systemDictionary.hpp

enum InitOption {
// 标记为Pre和Pre_JSR292的类会调用resolve_or_fail()函数进行预加载,
// 如果类不存在,报错
Pre,                       
    Pre_JSR292,                
// 标记为Opt、Opt_Only_JDK14NewRef和Opt_Only_JDK15的类会调用
// resolve_or_null()函数进行预加载,如果不存在,返回NULL
    Opt,                       
    Opt_Only_JDK14NewRef,      
    Opt_Only_JDK15,           
    OPTION_LIMIT,  // 5
    CEIL_LG_OPTION_LIMIT = 4  
  };

在宏WK_KLASSES_DO中定义每个需要加载的核心类时,也会指定InitOption的值,这些值会影响类加载的行为。

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

 

源代码位置:openjdk/hotspot/src/share/vm/classfile/systemDictionary.cpp

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()函数进行类的加载,最终会调用到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()函数的实现如下: 

源代码位置:openjdk/hotspot/src/share/vm/classfile/systemDictionary.cpp

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);
  // 如果之前已经产生了异常或klass为空,则抛出异常
  if (HAS_PENDING_EXCEPTION || klass == NULL) { 
     KlassHandle k_h(THREAD, 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为空也不会抛出异常。调用的resolve_or_null()函数的实现如下:

源代码位置:openjdk/hotspot/src/share/vm/classfile/systemDictionary.cpp

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()函数加载类。这两个函数在之前已经详细介绍过,这里不再介绍。

2、数组类型的预加载

这里还需要提示一下对元素类型为对象类型的一维或多维数组的处理逻辑,这些数组并不会预加载,而是需要通过触发来加载的。当一维或多维的元素类型是对象类型并且这些对象类型未加载时,需要触发加载。

源代码位置:openjdk/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;

        // ...
      }
    }
    // ...
}

元素类型为基本类型的一维数组的创建过程在2.1.5节介绍TypeArrayKlass时介绍过,这里不再介绍。有了元素类型为基本类型的一维数组后,可以方便地创建出多维数组。

创建出来的元素类型为基本类型的一维数组后会存储到类型为Klass*typeArrayKlassObjs数组中,这样就可以根据这些一维数组的TypeArrayKlass实例创建出多维数组。在2.1.5节介绍java_lang_Class::create_basic_type_mirror()函数时,调用了typeArrayKlassObj()函数,其实就是在获取这里创建出来的一维数组的实例。

对于元素类型为对象类型的一维或多维数组,只要创建出表示对象类型的InstanceKlass实例,就可以根据InstanceKlassObjArrayKlass中的一些字段表示出一维或多维数组,在之前介绍ObjArrayKlass时已经介绍过,例如创建Object一维数组。在Universe::genesis()函数中同样有如下调用:

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

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

 

 

 

  

 

posted on 2020-07-20 09:15  鸠摩(马智)  阅读(761)  评论(0编辑  收藏  举报

导航