• 启动类加载器 BootStrap ClassLoader :最顶层的类加载器,负责加载 JAVA_HOME\lib 目录中的,或通过-Xbootclasspath参数指定路径中的,且被虚拟机认可(按文件名识别,如rt.jar)的类。可以通 System.getProperty("sun.boot.class.path") 查看加载的路径。

  • 扩展类加载器 Extention ClassLoader :主要加载目录 %JRE_HOME%\lib\ext 目录下的 jar 包和 class 文件,或通过java.ext.dirs系统变量指定路径中的类库。也可以通过 System.out.println(System.getProperty("java.ext.dirs")) 查看加载类文件的路径。

  • 应用程序类加载器 Application ClassLoader :也叫做系统类加载器,可以通过 getSystemClassLoader() 获取,负责加载用户路径 classpath 上的类库。如果没有自定义类加载器,一般这个就是默认的类加载器。

类加载层次关系

Java类加载源码阅读_第1张图片

类加载器之间的这种层次关系叫做双亲委派模型。

双亲委派模型要求除了顶层的启动类加载器(Bootstrap ClassLoader)外,其余的类加载器都应当有自己的父类加载器。这里的类加载器之间的父子关系一般不是以继承关系实现的,而是用组合实现的。

  • 下面看一段源码

public class Launcher {    private static Launcher launcher = new Launcher();    private static String bootClassPath =        System.getProperty("sun.boot.class.path");

    public static Launcher getLauncher() {        return launcher;
    }    private ClassLoader loader;

    public Launcher() {        // Create the extension class loader
        ClassLoader extcl;        try {
            extcl = ExtClassLoader.getExtClassLoader();
        } catch (IOException e) {            throw new InternalError(                "Could not create extension class loader", e);
        }        // Now create the class loader to use to launch the application
        try {
            loader = AppClassLoader.getAppClassLoader(extcl);
        } catch (IOException e) {            throw new InternalError(                "Could not create application class loader", e);
        }        Thread.currentThread().setContextClassLoader(loader);
    }    /*
     * Returns the class loader used to launch the main application.
     */
    public ClassLoader getClassLoader() {        return loader;
    }    /*
     * The class loader used for loading installed extensions.
     */
    static class ExtClassLoader extends URLClassLoader {}    /**
      * The class loader used for loading from java.class.path.
      * runs in a restricted security context.
      */
    static class AppClassLoader extends URLClassLoader {}
复制代码

从源码中我们看到

(1) Launcher 初始化的时候创建了 ExtClassLoader 以及 AppClassLoader ,并将 ExtClassLoader 实例传入到 AppClassLoader 中。

(2)虽然上一段源码中没见到创建 BoopStrap ClassLoader ,但是程序一开始就执行了 System.getProperty("sun.boot.class.path") 。

附上 Launcher 相关文章: blog.csdn.net/jyxmust/art…

  • 类加载器中的继承关系 AppClassLoader 的父加载器为 ExtClassLoader , ExtClassLoader 的父加载器为 null , BoopStrap ClassLoader 为顶级加载器。

类加载机制-双亲委托

当JVM加载 Test.class 类的时候

  • 首先会到自定义加载器中查找,看是否已经加载过,如果已经加载过,则返回该类。

  • 如果自定义加载器没有加载过,则询问上一层加载器(即 AppClassLoader )是否已经加载过 Test.class 。

  • 如果没有加载过,则询问上一层加载器( ExtClassLoader )是否已经加载过。

  • 如果没有加载过,则继续询问上一层加载( BoopStrap ClassLoader )是否已经加载过。

  • 如果 BoopStrap ClassLoader 没有加载过,则到自己指定类加载路径 sun.boot.class.path 下查看是否有 Test.class 字节码,有则加载并返回加载后的类 c = findBootstrapClassOrNull(name) 。

  • 如果还是没找到调用 c = findClass(name) 到加载器 ExtClassLoader 指定的类加载路径 java.ext.dirs 下查找 class 文件,有则加载并返回类。

  • 依此类推,最后到自定义类加载器指定的路径还没有找到 Test.class 字节码,则抛出异常 ClassNotFoundException 。

这里注意

每个自定义的类加载器都需要重写 findClass 方法,该方法的作用是到指定位置查找 class文件并加载到JVM中,如果找不到则抛出 ClassNotFoundException 异常。

Java类加载源码阅读_第2张图片

双亲委派模型最大的好处就是让Java类同其类加载器一起具备了一种带优先级的层次关系。这句话可能不好理解,我们举个例子。比如我们要加载顶层的Java类—— java.lang.Object类,无论我们用哪个类加载器去加载 Object 类,这个加载请求最终都会委托给 Bootstrap ClassLoader ,这样就保证了所有加载器加载的 Object 类都是同一个类。

双亲委派模型的实现比较简单,在 java.lang.ClassLoader 的 loadClass 方法中:

protected Class loadClass(String name, boolean resolve)        throws ClassNotFoundException
    {        synchronized (getClassLoadingLock(name)) {            // First, check if the class has already been loaded
            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
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }            if (resolve) {
                resolveClass(c);
            }            return c;
        }
    }
复制代码
/**
     * Finds the class with the specified binary name.
     * This method should be overridden by class loader implementations that
     * follow the delegation model for loading classes, and will be invoked by
     * the {@link #loadClass loadClass} method after checking the
     * parent class loader for the requested class.  The default implementation
     * throws a ClassNotFoundException.
     *
     * @param  name
     *         The binary name of the class
     *
     * @return  The resulting Class object
     *
     * @throws  ClassNotFoundException
     *          If the class could not be found
     *
     * @since  1.2
     */
    protected Class findClass(String name) throws ClassNotFoundException {        throw new ClassNotFoundException(name);
    }
复制代码