Class 文件的装载流程 (类加载过程)

加载 -> 连接 (验证 -> 准备 -> 解析) -> 初始化 -> 使用 -> 卸载

加载

加载阶段,jvm 会通过类名获取到此类的字节码文件(.class 文件),然后将该文件中的数据结构转存到内存里(转化为运行时方法区内的数据结构),最后在堆中生成一个代表该类的 Class 对象,用于后期使用者创建对象或者调用相关方法。

验证

验证阶段用于保证 Class 文件符合 jvm 规范,如果验证失败会抛出 error。

准备

在该阶段虚拟机会给类对象的静态成员变量配置内存空间,并赋初始值。

解析

将类/接口/字段/方法中的号引用替换为直接引用。

初始化

虚拟机会调用类对象的初始化方法来进行类变量的赋值。

Class 文件被装载的条件

必须要有类去主动使用该 Class。方式有:使用 new 关键字、反射、克隆、反序列化;调用类的静态方法;调用一个类的子类的时候会初始化其父类;包含 main() 方法的类。被动使用则不会去装载 Class。方式有:调用了其父类的静态方法。

总结:

jvm 秉持了实用主义理念,对于没有用到的 Class 不会进行装载。但是在 java 代码的启动环节会加载一些使用到的类。

加载器种类

启动类加载器(Bootstrap ClassLoader):

在 jdk8 中用来加载 jvm 自身需要的类,c++ 实现,用来加载 rt.jar。在 jdk9 之后的 jdk 中,Bootstrap ClassLoader 主要用来加载 java.base 中的核心系统类。

扩展类加载器(ExtClassLoader):

jdk8 中用来加载 ${JAVA_HOME}/lib/ext 目录下的类。在 jdk9 中已经被移除。

模块加载器(PlatformClassLoader):

jdk9 之后用来代替 ExtClassLoader 的加载器,用来加载 jdk 中的非核心模块类。

应用程序类加载器(AppClassLoader):

用来加载一般的应用类。

自定义加载器:

使用者自己定义的,一般继承 java.lang.ClassLoader 的类。

双亲委派机制

任意一个 ClassLoader 在尝试加载一个类的时候,都会先尝试调用其父类的相关方法去加载类,如果其父类不能加载该类,则交由子类去完成。这样的好处:对于任意使用者自定义的 ClassLoader,都会先去尝试让 jvm 的 Bootstrap ClassLoader 去尝试加载(自定义的 ClassLoader 都继承了它们)。那么就能保证 jvm 的类会被优先加载,限制了使用者对 jvm 系统的影响。

源码

源码探究使用 jdk11,与 jdk8 中的有些许不同。

ClassLoader

ClassLoader 是类加载器的顶级父类,其核心的方法主要是 loadClass(...) 方法:

// ClassLoader.classprotected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{    // 加锁,保证线程安全    synchronized (getClassLoadingLock(name)) {        // 先去找一次 class 是否已经被加载了,如果已经被加载了就不用重复加载了        // 此方法的核心逻辑由 c++ 实现        Class<?> c = findLoadedClass(name);        // 没有被加载的情况        if (c == null) {            long t0 = System.nanoTime(); // 记录时间            try {                // 此处体现双亲委派机制                // 如果该加载器存在父加载器,就会先去调用父加载器的相关方法                // 如果没有父加载器,就去调用 Bootstrap 加载器                if (parent != null) {                    c = parent.loadClass(name, false);                } else {                    // 调用 BootstrapClassLoader,此方法的核心逻辑是 c++ 实现的                    c = findBootstrapClassOrNull(name);                }            } catch (ClassNotFoundException e) {            }            // 如果依旧加载不到,那么就说明父加载器仍然加载不到信息            // 那么就需要指定的加载器自己去加载了            if (c == null) {                long t1 = System.nanoTime();                                // 该加载器加载类文件的核心逻辑                // 该方法在 ClassLoader 中是留空的,需要子类按照自身的逻辑去实现                c = findClass(name);                // 此处做一些信息记录,和主逻辑无关                PerfCounter.getParentDelegationTime().addTime(t1 - t0);                PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);                PerfCounter.getFindClasses().increment();            }        }                if (resolve) {            // 解析 class,也是留空的,需要子类去实现            resolveClass(c);        }        return c;    }}

BuiltinClassLoader

BuiltinClassLoader 是 jdk9 中代替 URLClassLoader 的加载器,是 PlatformClassLoader 与 AppClassLoader 的父类。其继承了 SecureClassLoader,其核心的方法主要是 loadClassOrNull(...) 方法:

// BuiltinClassLoader.class// step 1@Overrideprotected Class<?> loadClass(String cn, boolean resolve) throws ClassNotFoundException{    // 复写了 loadClass(...) 方法,但是核心是调用 loadClassOrNull(...)    Class<?> c = loadClassOrNull(cn, resolve);    if (c == null)        throw new ClassNotFoundException(cn);    return c;}// step 2protected Class<?> loadClassOrNull(String cn, boolean resolve) {    // 加锁,保证线程安全    synchronized (getClassLoadingLock(cn)) {        // 先去找一次 class 是否已经被加载了,此方法是 ClassLoader 中的        Class<?> c = findLoadedClass(cn);        if (c == null) {            // 这里会需要去先加载模块信息            LoadedModule loadedModule = findLoadedModule(cn);            if (loadedModule != null) {                BuiltinClassLoader loader = loadedModule.loader();                if (loader == this) {                    if (VM.isModuleSystemInited()) {                        c = findClassInModuleOrNull(loadedModule, cn);                    }                } else {                    c = loader.loadClassOrNull(cn);                }            } else {                // 先调用父加载器的相关方法去加载一次                if (parent != null) {                    c = parent.loadClassOrNull(cn);                }                // 如果没加载到,则用当前加载器去加载                if (c == null && hasClassPath() && VM.isModuleSystemInited(){                    // 此方法内会调用到 defineClass(...) 方法去加载类文件                    c = findClassOnClassPathOrNull(cn);                }            }        }        // 解析 class        if (resolve && c != null)            resolveClass(c);        return c;    }}

该加载器中还有一个加载 class 字节码的方法:

// BuiltinClassLoader.classprivate Class<?> defineClass(String cn, Resource res) throws IOException{        URL url = res.getCodeSourceURL();    // 先解析这个 class 的路径    int pos = cn.lastIndexOf('.');    if (pos != -1) {        String pn = cn.substring(0, pos);        Manifest man = res.getManifest();        defineOrCheckPackage(pn, man, url);    }    // 这里会将 class 读取出来成一个 byte[] 字符串,并通过 jvm 的相关方法去加载    ByteBuffer bb = res.getByteBuffer();    if (bb != null) {        CodeSigner[] signers = res.getCodeSigners();        CodeSource cs = new CodeSource(url, signers);        // 该方法最后会调用 ClassLoader 内的 native 方法        return defineClass(cn, bb, cs);    } else {        byte[] b = res.getBytes();        CodeSigner[] signers = res.getCodeSigners();        CodeSource cs = new CodeSource(url, signers);        // 该方法最后会调用 ClassLoader 内的 native 方法        return defineClass(cn, b, 0, b.length, cs);    }}

BootClassLoader

BootClassLoader 是 ClassLoaders 的一个静态内部类,虽然它从代码实现上是 BuiltinClassLoader 的子类,但是从功能上说它是 PlatformClassLoader 的 parent 类:

// ClassLoader.classprivate static class BootClassLoader extends BuiltinClassLoader {    BootClassLoader(URLClassPath bcp) {        super(null, null, bcp);    }    // 复写了 BuiltinClassLoader 中的 loadClassOrNull(...) 方法    @Override    protected Class<?> loadClassOrNull(String cn) {        return JLA.findBootstrapClassOrNull(this, cn);    }};

PlatformClassLoader

PlatformClassLoader 也是 ClassLoaders 的一个静态内部类,从功能上说它是 BootClassLoader 的子类,同时也是 AppClassLoader 的 parent 类。PlatformClassLoader 主要用来加载一些 module:

// ClassLoader.classprivate static class PlatformClassLoader extends BuiltinClassLoader {    static {        if (!ClassLoader.registerAsParallelCapable())            throw new InternalError();    }    // 此处会将 BootClassLoader 作为 parent 参数传入进去    PlatformClassLoader(BootClassLoader parent) {        super("platform", parent, null);    }    // 加载 module    private Package definePackage(String pn, Module module) {        return JLA.definePackage(this, pn, module);    }}

AppClassLoader

AppClassLoader 的核心方法是 loadClass(...),最终会调用到 BuiltinClassLoader.loadClassOrNull(...) 方法,而此方法内部又会调用到 PlatformClassLoader.loadClass(...) 方法;然后实际上 PlatformClassLoader 内部又会去调用 BootClassLoader 的 loadClassOrNull(...) 方法。这种方式下就完成类加载器的双亲委派机制:

// ClassLoader.classprivate static class AppClassLoader extends BuiltinClassLoader {    static {        if (!ClassLoader.registerAsParallelCapable())            throw new InternalError();    }    final URLClassPath ucp;    // 此处会将 PlatformClassLoader 作为 parent 参数传入进去    AppClassLoader(PlatformClassLoader parent, URLClassPath ucp) {        super("app", parent, ucp);        this.ucp = ucp;    }    @Override    protected Class<?> loadClass(String cn, boolean resolve) throws ClassNotFoundException{        SecurityManager sm = System.getSecurityManager();        if (sm != null) {            int i = cn.lastIndexOf('.');            if (i != -1) {                sm.checkPackageAccess(cn.substring(0, i));            }        }        // 实际上是调用了 BuiltinClassLoader.loadClassOrNull(...) 方法        return super.loadClass(cn, resolve);    }    @Override    protected PermissionCollection getPermissions(CodeSource cs) {        PermissionCollection perms = super.getPermissions(cs);        perms.add(new RuntimePermission("exitVM"));        return perms;    }    void appendToClassPathForInstrumentation(String path) {        ucp.addFile(path);    }    private Package definePackage(String pn, Module module) {        return JLA.definePackage(this, pn, module);    }    protected Package defineOrCheckPackage(String pn, Manifest man, URL url) {        return super.defineOrCheckPackage(pn, man, url);    }}