当前位置: 代码迷 >> 综合 >> HotSpot 类加载
  详细解决方案

HotSpot 类加载

热度:70   发布时间:2024-02-05 17:34:19.0

上一篇我们从使用一个类来断点分析了HotSpot的启动流程。今天我们来看HotSpot的类加载。首先必须明确的是在HotSpot启动的过程中,在init_globals()函数中universe2_init()函数中已经预加载了系统运行必须的Java类。所以我们先分析预加载的系统运行必须的类,然后再看JVM启动以后我们的Hello类是如何加载的。

1. HotSpot内部预加载类

在开始分析之前需要了解HotSpot中几个C++类,vmSymbols.cpp使用宏定义类诸如java_lang_System等类路径和名称,还有一些虚拟机为加快运行预定义的符号。systemDictionary.cpp宏定义了klass映射。

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

先来看看 VM_SYMBOLS_DO,它在头文件中是个宏定义
hotspot/src/share/vm/classfile/vmSymbols.hpp

#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")                         \......    

再来看看 new_permanent_symbol,符号表维护着Java类/字符等信息
hotspot/src/share/vm/classfile/symbolTable.cpp

Symbol* SymbolTable::new_permanent_symbol(const char* name, TRAPS) {unsigned int hash;//从符号表中查找符号应用,SymbolTable是HashTableSymbol* 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对象模型的Klass
hotspot/src/share/vm/classfile/systemDictionary.cpp

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 classesinitialize_preloaded_classes(CHECK);
}

预加载类,包括符号表中定义的类,javabase模块下的类,java基本类型
hotspot/src/share/vm/classfile/systemDictionary.cpp

void SystemDictionary::initialize_preloaded_classes(TRAPS) {//为Javabase模块创建ModuleEntry ClassLoader::classLoader_init2(CHECK);// 预加载类WKID scan = FIRST_WKID;....//类加载// JSR 292 classesWKID 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

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 classreturn ((*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 loadingk = load_instance_class(name, class_loader, THREAD);
}                                                      

hotspot/src/share/vm/classfile/systemDictionary.cpp
九曲十八弯,实际调用加载的地方

nstanceKlassHandle SystemDictionary::load_instance_class(Symbol* class_name, Handle class_loader, TRAPS) {if (k.is_null()) {// Use VM class loaderk = ClassLoader::load_class(class_name, search_only_bootloader_append, CHECK_(nh));}
}

hotspot/src/share/vm/classfile/classLoader.cpp
创建字节码文件流,每个被加载的Java类都对应着一个ClassLoaderData结构,ClassLoaderData内部通过链表维护者加载卸载的类

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解析字节码文件

instanceKlassHandle KlassFactory::create_from_stream(ClassFileStream* stream,Symbol*name, ...) {//调用类解析instanceKlassHandle result = parser.create_instance_klass(old_stream != stream, CHECK_NULL);return result;}

2. 应用类加载

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

 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 nameString   cn = what; //简化case// load the main classcn = 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类Klass* klass = SystemDictionary::resolve_or_fail(name, loader, protection_domain,throwError != 0, CHECK_NULL);return (jclass) JNIHandles::make_local(env, klass_handle->java_mirror());
}

3.HotSpot类加载模型

jdk/src/java.base/share/classes/java/lang/ClassLoader.java
上面在加载Hello类是首先去获取类加载器,按初始化等级返回相应的类加载器,在VM.java中定义了各等级的含义:JAVA_LANG_SYSTEM_INITED = 1,lang库初始化结束,MODULE_SYSTEM_INITED = 2模块初始化结束,SYSTEM_LOADER_INITIALIZING = 3 初始化中,SYSTEM_BOOTED= 4 系统完全启动,显然加载Hello类时初始化等级为4 scl为ClassLoader,scl在initSystemClassLoader中被赋值,initSystemClassLoader在HotSpot启动阶段被调用,所以scl不为空。

 @CallerSensitivepublic 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 initializedassert 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-PathURLClassPath 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 loadersBOOT_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 { //内部类@Overrideprotected 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

@Overrideprotected 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 moduleBuiltinClassLoader 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 herePrivilegedAction<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 VMreturn defineClass(cn, bb, cs); //调用顶级父类ClassLoder的defineClass} finally {reader.release(bb);}} catch (IOException ioe) {// TBD on how I/O errors should be propagatedreturn 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 statsPerfCounter.getParentDelegationTime().addTime(t1 - t0);PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c); //解析}return c;}}

本篇主要从类加载的角度来看HotSpot内部预加载类和用户类的加载以及HotSpot的类加载模型,虚拟机的启动是通过引导类加载器创建一个初始化类来完成的。虚拟机的创建和类的加载为后续类的链接初始化等做好准备的。类的链接(验证,准备,解析)是基于二进制字节码文件的。所以下一篇我们看Java字节码文件。

  相关解决方案