天天看點

HotSpot 類加載

HotSpot啟動的過程中,在init_globals()函數中universe2_init()函數中已經預加載了系統運作必須的Java類。是以我們先分析預加載的系統運作必須的類,然後再看JVM啟動以後我們的Hello類是如何加載的。JVM的組成如下

HotSpot 類加載

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位元組碼檔案