HotSpot 类加载

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值