HotSpot啟動的過程中,在init_globals()函數中universe2_init()函數中已經預加載了系統運作必須的Java類。是以我們先分析預加載的系統運作必須的類,然後再看JVM啟動以後我們的Hello類是如何加載的。JVM的組成如下
1. 方法區:儲存Java類中繼資料,線程共享的運作時記憶體區域
2. 堆:所有類執行個體和數組對象配置設定記憶體的區域
3. Java棧:每個線程私有,與傳統c棧類似,存儲局部變量,方法棧幀等
4. pc寄存器:每個線程私有,程式運作計數指針
5. 本地方法棧:傳統c棧
6. 類加載器:加載Java位元組碼
7. 執行引擎:位元組碼解釋器/模闆解釋器
虛拟機啟動以後會加載指定的初始化主類,并執行主類包含的main方法,主類運作結束以後虛拟機将會退出,一般伺服器tomcat會有一個主線循環讓虛拟機不退出。Java程式在虛拟機運作的第一步就是類加載。
一. HotSpot内部預加載類
hotspot/src/share/vm/memory/universe.cpp
void universe2_init() {
......
Universe::genesis(CATCH);
}
hotspot/src/share/vm/memory/universe.cpp
void Universe::genesis(TRAPS) {
//首先是基本型的初始化
.....
//初始化符号表
vmSymbols::initialize(CHECK);
//初始化系統字典
SystemDictionary::initialize(CHECK);
}
hotspot/src/share/vm/classfile/vmSymbols.cpp
//注意這裡的 VM_SYMBOLS_DO 符号
#define VM_SYMBOL_BODY(name, string) string "\0"
static const char* vm_symbol_bodies = VM_SYMBOLS_DO(VM_SYMBOL_BODY, VM_ALIAS_IGNORE);
void vmSymbols::initialize(TRAPS) {
if (!UseSharedSpaces) {
//vm_symbol_bodies聲明在上面
const char* string = &vm_symbol_bodies[0];
for (int index = (int)FIRST_SID; index < (int)SID_LIMIT; index++) {
//為Java類建立符号
Symbol* sym = SymbolTable::new_permanent_symbol(string, CHECK);
//存到符号數組中
_symbols[index] = sym;
string += strlen(string); // skip string body
//下一個
string += 1; // skip trailing null
}
//Java基本類型
_type_signatures[T_BYTE] = byte_signature();
.....
// no single signatures for T_OBJECT or T_ARRAY
}
}
hotspot/src/share/vm/classfile/vmSymbols.hpp
先來看看 VM_SYMBOLS_DO,它在頭檔案中是個宏定義
#define VM_SYMBOLS_DO(template, do_alias) \
/* commonly used class, package, module names */ \
template(java_base, JAVA_BASE_NAME) \
template(java_lang_System, "java/lang/System") \
template(java_lang_Object, "java/lang/Object") \
template(java_lang_Class, "java/lang/Class") \
template(java_lang_Package, "java/lang/Package") \
template(java_lang_Module, "java/lang/Module") \
......
hotspot/src/share/vm/classfile/symbolTable.cpp
再來看看 new_permanent_symbol,符号表維護着Java類/字元等資訊
Symbol* SymbolTable::new_permanent_symbol(const char* name, TRAPS) {
unsigned int hash;
//從符号表中查找符号應用,SymbolTable是HashTable
Symbol* result = SymbolTable::lookup_only((char*)name, (int)strlen(name), hash);
if (result != NULL) {
return result;
}
//如果不存在則建立hash索引,并放到表中
SymbolTable* table = the_table();
int index = table->hash_to_index(hash);
return table->basic_add(index, (u1*)name, (int)strlen(name), hash, false, THREAD);
}
hotspot/src/share/vm/classfile/systemDictionary.cpp
然後是系統字典初始化,系統字典維護者HotSpot對象模型的Klass,initialize_preloaded_classes執行預加載,主要是javabase子產品下的類
void SystemDictionary::initialize(TRAPS) {
// Allocate arrays
_dictionary = new Dictionary(calculate_systemdictionary_size(PredictedLoadedClassCount));
_placeholders = new PlaceholderTable(_nof_buckets);
_number_of_modifications = 0;
_loader_constraints = new LoaderConstraintTable(_loader_constraint_size);
_resolution_errors = new ResolutionErrorTable(_resolution_error_size);
_invoke_method_table = new SymbolPropertyTable(_invoke_method_size);
// Allocate private object used as system class loader lock
_system_loader_lock_obj = oopFactory::new_intArray(0, CHECK);
// Initialize basic classes
initialize_preloaded_classes(CHECK);
}
hotspot/src/share/vm/classfile/systemDictionary.cpp
預加載類,包括符号表中定義的類,javabase子產品下的類,java基本類型等
void SystemDictionary::initialize_preloaded_classes(TRAPS) {
//為Javabase子產品建立ModuleEntry
ClassLoader::classLoader_init2(CHECK);
// 預加載類
WKID scan = FIRST_WKID;
....
//類加載
// JSR 292 classes
WKID jsr292_group_start = WK_KLASS_ENUM_NAME(MethodHandle_klass);
WKID jsr292_group_end = WK_KLASS_ENUM_NAME(VolatileCallSite_klass);
initialize_wk_klasses_until(jsr292_group_start, scan, CHECK);
initialize_wk_klasses_through(jsr292_group_end, scan, CHECK);
//其他類加載
//Java基本類型int等
....
}
hotspot/src/share/vm/classfile/systemDictionary.cpp
取initialize_wk_klasses_until加載路徑來看
void SystemDictionary::initialize_wk_klasses_until(WKID limit_id, WKID &start_id, TRAPS) {
for (int id = (int)start_id; id < (int)limit_id; id++) {
//
initialize_wk_klass((WKID)id, opt, CHECK);
}
}
hotspot/src/share/vm/classfile/systemDictionary.cpp
bool SystemDictionary::initialize_wk_klass(WKID id, int init_opt, TRAPS) {
//查符号表
Symbol* symbol = vmSymbols::symbol_at((vmSymbols::SID)sid);
InstanceKlass** klassp = &_well_known_klasses[id];
k = resolve_or_fail(symbol, true, CHECK_0); // load required class
return ((*klassp) != NULL);
}
hotspot/src/share/vm/classfile/systemDictionary.cpp
Klass* SystemDictionary::resolve_or_fail(Symbol* class_name, bool throw_error, TRAPS)
{
return resolve_or_fail(class_name, Handle(), Handle(), throw_error, THREAD);
}
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);
......
return klass;
}
hotspot/src/share/vm/classfile/systemDictionary.cpp
Klass* SystemDictionary::resolve_or_null(Symbol* class_name, ...) {
//走了這裡
return resolve_instance_class_or_null(class_name, class_loader, protection_domain, THREAD);
}
hotspot/src/share/vm/classfile/systemDictionary.cpp
Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, ...) {
// Do actual loading
k = load_instance_class(name, class_loader, THREAD);
}
hotspot/src/share/vm/classfile/systemDictionary.cpp
九曲十八彎,實際調用加載的地方,由ClassLoader去加載類
nstanceKlassHandle SystemDictionary::load_instance_class(Symbol* class_name, Handle class_loader, TRAPS) {
if (k.is_null()) {
// Use VM class loader
k = ClassLoader::load_class(class_name, search_only_bootloader_append, CHECK_(nh));
}
}
hotspot/src/share/vm/classfile/classLoader.cpp
建立位元組碼檔案流,每個被加載的Java類都對應着一個ClassLoaderData結構,ClassLoaderData内部通過連結清單維護着ClassLoader和ClassLoader加載的類
instanceKlassHandle ClassLoader::load_class(Symbol* name, bool search_append_only, TRAPS) {
stream = search_module_entries(_exploded_entries, class_name, file_name, CHECK_NULL);
ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
instanceKlassHandle result = KlassFactory::create_from_stream(stream, name, ...);
}
hotspot/src/share/vm/classfile/klassFactory.cpp
最終調用ClassFileParser解析Java位元組碼檔案流,位元組碼檔案如何解析後面再談。
instanceKlassHandle KlassFactory::create_from_stream(ClassFileStream* stream,Symbol*name, ...) {
//調用類解析
ClassFileParser parser(stream,name,loader_data,protection_domain,host_klass,cp_patches,
ClassFileParser::BROADCAST, // publicity level
CHECK_NULL);
//建立instanceKclass,儲存解析結果
instanceKlassHandle result = parser.create_instance_klass(old_stream != stream, CHECK_NULL);
return result;
}
二. 應用類加載
jdk/src/java.base/share/native/libjli/java.c
在HotSpot啟動以後,加載我們的Hello類并調用main方法
int JNICALL JavaMain(void * _args){
//虛拟機啟動
if (!InitializeJVM(&vm, &env, &ifn)) {
JLI_ReportErrorMessage(JVM_ERROR1);
exit(1);
}
......
//加載主類即我們的Hello類
mainClass = LoadMainClass(env, mode, what);
//擷取Hello類的main方法
mainID = (*env)->GetStaticMethodID(env, mainClass, "main","([Ljava/lang/String;)V");
//調用main方法
(*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
}
在加載Hello類之前先加載LancherHelper類,由LancherHelper去加載Hello類
static jclass LoadMainClass(JNIEnv *env, int mode, char *name){
//LancherHelper類
jclass cls = GetLauncherHelperClass(env);
//擷取LancherHelper類的checkAndLoadMain方法
NULL_CHECK0(mid = (*env)->GetStaticMethodID(env, cls,"checkAndLoadMain",
"(ZILjava/lang/String;)Ljava/lang/Class;"));
NULL_CHECK0(str = NewPlatformString(env, name));
//使用checkAndLoadMain加載Hello類
NULL_CHECK0(result = (*env)->CallStaticObjectMethod(env, cls, mid,USE_STDERR, mode, str));
return (jclass)result;
}
先去加載LancherHelper類
jclass GetLauncherHelperClass(JNIEnv *env)
{
if (helperClass == NULL) {
NULL_CHECK0(helperClass = FindBootStrapClass(env,
"sun/launcher/LauncherHelper"));
}
return helperClass;
}
jdk/src/java.base/unix/native/libjli/java_md_common.c
jclass FindBootStrapClass(JNIEnv *env, const char* classname)
{
if (findBootClass == NULL) {
//擷取jvm.cpp中的JVM_FindClassFromBootLoader方法
findBootClass = (FindClassFromBootLoader_t *)dlsym(RTLD_DEFAULT,
"JVM_FindClassFromBootLoader");
}
return findBootClass(env, classname); //調用JVM_FindClassFromBootLoader方法
}
hotspot/src/share/vm/prims/jvm.cpp
調用SystemDictionary解析類去加載類,流程與内部預加載類的加載機制一緻
JVM_ENTRY(jclass, JVM_FindClassFromBootLoader(JNIEnv* env,
const char* name))
//調用SystemDictionary解析類去加載類,流程與内部預加載類的加載機制一緻
Klass* k = SystemDictionary::resolve_or_null(h_name, CHECK_NULL);
return (jclass) JNIHandles::make_local(env, k->java_mirror());
JVM_END
jdk/src/java.base/share/classes/sun/launcher/LauncherHelper.java
加載Hello類
public static Class<?> checkAndLoadMain(boolean printToStderr,
int mode,
String what) {
//斷點顯示mode=1,走loadMainClass
Class<?> mainClass = (mode == LM_MODULE) ? loadModuleMainClass(what)
: loadMainClass(mode, what);
// record the real main class for UI purposes
// neither method above can return null, they will abort()
appClass = mainClass;
validateMainClass(mainClass);
return mainClass;
}
使用類加載器加載Hello類,mode=1,what為類名即Hello
private static Class<?> loadMainClass(int mode, String what) {
// get the class name
String cn = what; //簡化case
// load the main class
cn = cn.replace('/', '.');
Class<?> mainClass = null;
ClassLoader scl = ClassLoader.getSystemClassLoader(); //擷取類加載器
try {
try {
mainClass = Class.forName(cn, false, scl); //加載類
} catch (NoClassDefFoundError | ClassNotFoundException cnfe) {
......
}
} catch (LinkageError le) {
......
}
return mainClass;
}
jdk/src/java.base/share/classes/java/lang/Class.java
Class.forName将進行安全校驗并調用Class.c中的forName0
@CallerSensitive
public static Class<?> forName(String name, boolean initialize,
ClassLoader loader) throws ClassNotFoundException
{
Class<?> caller = null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// Reflective call to get caller class is only needed if a security manager
// is present. Avoid the overhead of making this call otherwise.
caller = Reflection.getCallerClass();
if (VM.isSystemDomainLoader(loader)) {
ClassLoader ccl = ClassLoader.getClassLoader(caller);
if (!VM.isSystemDomainLoader(ccl)) {
sm.checkPermission(
SecurityConstants.GET_CLASSLOADER_PERMISSION);
}
}
}
return forName0(name, initialize, loader, caller); //forName0是一個native調用
}
jdk/src/java.base/share/native/libjava/Class.c
JNIEXPORT jclass JNICALL
Java_java_lang_Class_forName0(JNIEnv *env, jclass this, jstring classname,
jboolean initialize, jobject loader, jclass caller){
cls = JVM_FindClassFromCaller(env, clname, initialize, loader, caller);
}
hotspot/src/share/vm/prims/jvm.cpp
JVM_ENTRY(jclass, JVM_FindClassFromCaller(JNIEnv* env, const char* name,
jboolean init, jobject loader,
jclass caller)){
jclass result = find_class_from_class_loader(env, h_name, init, h_loader,
h_prot, false, THREAD);
}
hotspot/src/share/vm/prims/jvm.cpp
jclass find_class_from_class_loader(JNIEnv* env, Symbol* name, jboolean init,
Handle loader, Handle protection_domain,
jboolean throwError, TRAPS) {
//加載Hello類,在前面加載預加載類時也是走resolve_or_fail
Klass* klass = SystemDictionary::resolve_or_fail(name, loader, protection_domain,throwError != 0, CHECK_NULL);
return (jclass) JNIHandles::make_local(env, klass_handle->java_mirror());
}
三.HotSpot類加載模型
jdk/src/java.base/share/classes/java/lang/ClassLoader.java
擷取類加載器,按初始化等級傳回相應的類加載器,在VM.java中定義了各等級的含義:
1. JAVA_LANG_SYSTEM_INITED = 1,lang庫初始化結束,
2. MODULE_SYSTEM_INITED = 2子產品初始化結束,
3. SYSTEM_LOADER_INITIALIZING = 3 初始化中,
4. SYSTEM_BOOTED= 4 系統完全啟動,
顯然加載Hello類時初始化等級為4 scl為ClassLoader,scl在initSystemClassLoader中被指派,initSystemClassLoader在HotSpot啟動階段被調用,是以scl不為空。
@CallerSensitive
public static ClassLoader getSystemClassLoader() {
switch (VM.initLevel()) {
case 0:
case 1:
case 2:
return getBuiltinAppClassLoader();
case 3:
String msg = "getSystemClassLoader should only be called after VM booted";
throw new InternalError(msg);
case 4:
// system fully initialized
assert VM.isBooted() && scl != null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkClassLoaderPermission(scl, Reflection.getCallerClass());
}
return scl;
default:
throw new InternalError("should not reach here");
}
}
jdk/src/java.base/share/classes/java/lang/ClassLoader.java
擷取類加載器
static ClassLoader getBuiltinAppClassLoader() {
return ClassLoaders.appClassLoader();
}
jdk/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java
ClassLoaders中實作了三種類加載器:BootClassLoader,PlatformClassLoader,AppClassLoader,均繼承自BuiltinClassLoader,間接繼承自SecureClassLoader,ClassLoader(抽象類);
static {
// -Xbootclasspth/a or -javaagent Boot-Class-Path
URLClassPath bcp = null;
String s = VM.getSavedProperty("jdk.boot.class.path.append");
if (s != null && s.length() > 0)
bcp = toURLClassPath(s);
// we have a class path if -cp is specified or -m is not specified.
// If neither is specified then default to -cp <working directory>
// If -cp is not specified and -m is specified, the value of
// java.class.path is an empty string, then no class path.
URLClassPath ucp = new URLClassPath(new URL[0]);
String mainMid = System.getProperty("jdk.module.main");
String cp = System.getProperty("java.class.path");
if (cp == null)
cp = "";
if (mainMid == null || cp.length() > 0)
addClassPathToUCP(cp, ucp);
// create the class loaders
BOOT_LOADER = new BootClassLoader(bcp);
PLATFORM_LOADER = new PlatformClassLoader(BOOT_LOADER);
APP_LOADER = new AppClassLoader(PLATFORM_LOADER, ucp);
}
jdk/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java
内建類加載器AppClassLoader如何加載類
private static class AppClassLoader extends BuiltinClassLoader { //内部類
@Override
protected Class<?> loadClass(String cn, boolean resolve)
throws ClassNotFoundException
{
// for compatibility reasons, say where restricted package list has
// been updated to list API packages in the unnamed module.
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
int i = cn.lastIndexOf('.');
if (i != -1) {
sm.checkPackageAccess(cn.substring(0, i));
}
}
return super.loadClass(cn, resolve); //調用父類
}
}
jdk/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java
@Override
protected Class<?> loadClass(String cn, boolean resolve)
throws ClassNotFoundException
{
Class<?> c = loadClassOrNull(cn, resolve);
if (c == null)
throw new ClassNotFoundException(cn);
return c;
}
jdk/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java
protected Class<?> loadClassOrNull(String cn, boolean resolve) {
synchronized (getClassLoadingLock(cn)) {
//檢查類是否已加載
Class<?> c = findLoadedClass(cn);
if (c == null) { //沒找到
// 定位子產品
LoadedModule loadedModule = findLoadedModule(cn);
if (loadedModule != null) {
// package is in a module
BuiltinClassLoader loader = loadedModule.loader();
if (loader == this) {
if (VM.isModuleSystemInited()) {
c = findClassInModuleOrNull(loadedModule, cn);
}
} else {
//代理到其他加載器
c = loader.loadClassOrNull(cn);
}
} else {
// 調用父加載器這裡指BootClassLoader和PlatformClassLoader,parent由構造器傳入
if (parent != null) {
c = parent.loadClassOrNull(cn);
}
//父加載沒加載,則由目前加載器加載,
if (c == null && hasClassPath() && VM.isModuleSystemInited()) {
c = findClassOnClassPathOrNull(cn);
}
}
}
if (resolve && c != null)
resolveClass(c); //解析類
return c;
}
}
jdk/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java
private Class<?> findClassOnClassPathOrNull(String cn) {
String path = cn.replace('.', '/').concat(".class");
if (System.getSecurityManager() == null) {
Resource res = ucp.getResource(path, false);
if (res != null) {
try {
return defineClass(cn, res);
} catch (IOException ioe) {
// TBD on how I/O errors should be propagated
}
}
return null;
} else {
// avoid use of lambda here
PrivilegedAction<Class<?>> pa = new PrivilegedAction<>() {
public Class<?> run() {
Resource res = ucp.getResource(path, false);
if (res != null) {
try {
return defineClass(cn, res);
} catch (IOException ioe) {
// TBD on how I/O errors should be propagated
}
}
return null;
}
};
return AccessController.doPrivileged(pa);
}
}
jdk/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java
private Class<?> defineClass(String cn, LoadedModule loadedModule) {
try {
ByteBuffer bb = null;
URL csURL = null;
......
CodeSource cs = new CodeSource(csURL, (CodeSigner[]) null);
try {
// define class to VM
return defineClass(cn, bb, cs); //調用頂級父類ClassLoder的defineClass
} finally {
reader.release(bb);
}
} catch (IOException ioe) {
// TBD on how I/O errors should be propagated
return null;
}
}
jdk/src/java.base/share/classes/java/lang/ClassLoader.java
自定義類加載器加載類時可以不繼承自内建類加載器,直接繼承自ClassLoader重寫loadClass和findClass方法,雙親委派模型可以被破壞
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
jdk/src/java.base/share/classes/java/lang/ClassLoader.java
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 檢查是否已加載過
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false); //父類加載
} else {
c = findBootstrapClassOrNull(name); //從啟動類中查找
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name); //查找
// this is the defining class loader; record the stats
PerfCounter.getParentDelegationTime().addTime(t1 - t0);
PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c); //解析
}
return c;
}
}
本篇主要從類加載的角度來看HotSpot内部預加載類和使用者類的加載以及HotSpot的類加載模型,虛拟機的啟動是通過引導類加載器建立一個初始化類來完成的。虛拟機的建立和類的加載為後續類的連結初始化等做好準備的。類的連結(驗證,準備,解析)是基于二進制位元組碼檔案的。是以下一篇我們看Java位元組碼檔案