查看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的源代码
13、类加载器
14、类的双亲委派机制
作者持续维护的个人博客classloading.com。
关注公众号,有HotSpot源码剖析系列文章!