classloader-launcher source code analysis

sun.misc.launcher

The entry of the java virtual machine

Launcher initialization

private static URLStreamHandlerFactory factory = new Launcher.Factory();
private static Launcher launcher = new Launcher();
private static String bootClassPath = System.getProperty("sun.boot.class.path");
private ClassLoader loader;
private static URLStreamHandler fileHandler;

public Launcher() {
    Launcher.ExtClassLoader var1;
    try {
        var1 = Launcher.ExtClassLoader.getExtClassLoader();
    } catch (IOException var10) {
        throw new InternalError("Could not create extension class loader", var10);
    }

    try {
        this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
    } catch (IOException var9) {
        throw new InternalError("Could not create application class loader", var9);
    }

    Thread.currentThread().setContextClassLoader(this.loader);
    String var2 = System.getProperty("java.security.manager");
    if (var2 != null) {
        SecurityManager var3 = null;
        if (!"".equals(var2) && !"default".equals(var2)) {
            try {
                var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
            } catch (IllegalAccessException var5) {
            } catch (InstantiationException var6) {
            } catch (ClassNotFoundException var7) {
            } catch (ClassCastException var8) {
            }
        } else {
            var3 = new SecurityManager();
        }

        if (var3 == null) {
            throw new InternalError("Could not create SecurityManager: " + var2);
        }

        System.setSecurityManager(var3);
    }

}
复制代码

From this code of the Launcher constructor, we can get the following information:

  1. Launcher initializes ExtClassLoader and AppClassloader, and uses ExtClassLoader as the parent loader of AppClassloader (note that this is not an inheritance relationship)
  2. What you can see here is that BootstrapClassloader is not explicitly used as the parent loader of ExtClassloader. But we can see from the loadClass method of the classloader that the native method is called, which is actually obtained from the BootstrapClassloader (because the BootstrapClassloader is actually a C++ implementation and does not inherit the Classloader)
  3. Set the thread's default classloader to AppClassloader
  4. BootstrapClassloader is not initialized here, but there can be code like System.getProperty("sun.boot.class.path") to load some packages.

Output the content of System.getProperty("sum.boot.class.path"), we can see the content of bootstrapClassloader. You can also get path information through sun.misc.Launcher.getBootstrapClassPath()

 ==> file:/C:/Program%20Files/Java/jdk1.8.0_201/jre/lib/resources.jar
 ==> file:/C:/Program%20Files/Java/jdk1.8.0_201/jre/lib/rt.jar
 ==> file:/C:/Program%20Files/Java/jdk1.8.0_201/jre/lib/sunrsasign.jar
 ==> file:/C:/Program%20Files/Java/jdk1.8.0_201/jre/lib/jsse.jar
 ==> file:/C:/Program%20Files/Java/jdk1.8.0_201/jre/lib/jce.jar
 ==> file:/C:/Program%20Files/Java/jdk1.8.0_201/jre/lib/charsets.jar
 ==> file:/C:/Program%20Files/Java/jdk1.8.0_201/jre/lib/jfr.jar
 ==> file:/C:/Program%20Files/Java/jdk1.8.0_201/jre/classes
复制代码

There is also a BootClassPathHolder initialized here

private static class BootClassPathHolder {
    static final URLClassPath bcp;

    private BootClassPathHolder() {
    }

    static {
        URL[] var0;
        if (Launcher.bootClassPath != null) {
            var0 = (URL[])AccessController.doPrivileged(new PrivilegedAction<URL[]>() {
                public URL[] run() {
                    File[] var1 = Launcher.getClassPath(Launcher.bootClassPath);
                    int var2 = var1.length;
                    HashSet var3 = new HashSet();

                    for(int var4 = 0; var4 < var2; ++var4) {
                        File var5 = var1[var4];
                        if (!var5.isDirectory()) {
                            var5 = var5.getParentFile();
                        }

                        if (var5 != null && var3.add(var5)) {
                            MetaIndex.registerDirectory(var5);
                        }
                    }

                    return Launcher.pathToURLs(var1);
                }
            });
        } else {
            var0 = new URL[0];
        }

        bcp = new URLClassPath(var0, Launcher.factory, (AccessControlContext)null);
        bcp.initLookupCache((ClassLoader)null);
    }
}
复制代码

Here is my analysis:

  1. When BootClassPath (ie System.getProperty("sum.boot.class.path")), the executor reads these files and loads them as File[]
  2. Traverse file[] to determine whether it is a path, if not, get the upper-level path (D:/test/1.jar => D:/test)
  3. 当不为null时,将其添加到set(用于去重)并判断是否添加成功。如果添加成功,则进入到MetaIndex.registerDirectory将路径传输进去,读取目录下meta-index(用来提供jvm启动的加载速度),并添加到jarMap里面。
  4. initLookupCache这个方法将查找到的classPath缓存到jvm中。其中null对应到的是bootstrapClassloader。这里后面有时间可以深入一下jvm的源码。

ExtClassloader

这里提取了部分代码

private static Launcher.ExtClassLoader createExtClassLoader() throws IOException {
    try {
        return (Launcher.ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<Launcher.ExtClassLoader>() {
            public Launcher.ExtClassLoader run() throws IOException {
                File[] var1 = Launcher.ExtClassLoader.getExtDirs();
                int var2 = var1.length;

                for(int var3 = 0; var3 < var2; ++var3) {
                    MetaIndex.registerDirectory(var1[var3]);
                }

                return new Launcher.ExtClassLoader(var1);
            }
        });
    } catch (PrivilegedActionException var1) {
        throw (IOException)var1.getException();
    }
}
public ExtClassLoader(File[] var1) throws IOException {
    super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
    SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
}
private static URL[] getExtURLs(File[] var0) throws IOException {
    Vector var1 = new Vector();

    for(int var2 = 0; var2 < var0.length; ++var2) {
        String[] var3 = var0[var2].list();
        if (var3 != null) {
            for(int var4 = 0; var4 < var3.length; ++var4) {
                if (!var3[var4].equals("meta-index")) {
                    File var5 = new File(var0[var2], var3[var4]);
                    var1.add(Launcher.getFileURL(var5));
                }
            }
        }
    }

    URL[] var6 = new URL[var1.size()];
    var1.copyInto(var6);
    return var6;
}
复制代码

这里可以看出几点:

  1. ExtClassloader 的父加载器为null
  2. 加载路径为java.ext.dirs
  3. 初始化方式与BootClassPathHolder有相似的地方,都采用了meta-index记载
  4. ExtClassloader 继承了 URLClassloader

AppClassLoader

public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
    final String var1 = System.getProperty("java.class.path");
    final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
    return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
        public Launcher.AppClassLoader run() {
            URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
            return new Launcher.AppClassLoader(var1x, var0);
        }
    });
}

public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
    int var3 = var1.lastIndexOf(46);
    if (var3 != -1) {
        SecurityManager var4 = System.getSecurityManager();
        if (var4 != null) {
            var4.checkPackageAccess(var1.substring(0, var3));
        }
    }

    if (this.ucp.knownToNotExist(var1)) {
        Class var5 = this.findLoadedClass(var1);
        if (var5 != null) {
            if (var2) {
                this.resolveClass(var5);
            }

            return var5;
        } else {
            throw new ClassNotFoundException(var1);
        }
    } else {
        return super.loadClass(var1, var2);
    }
}
复制代码

这里可以看出:

  1. AppClassloader 加载路径为java.class.path
  2. 重载了一下loadClass方法
    • 校验class文件
    • 查找类是否存在

Guess you like

Origin juejin.im/post/7102054960933634084