classloader-launcherソースコード分析

sun.misc.launcher

Java仮想マシンのエントリ

ランチャーの初期化

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);
    }

}
复制代码

Launcherコンストラクターのこのコードから、次の情報を取得できます。

  1. LauncherはExtClassLoaderとAppClassloaderを初期化し、ExtClassLoaderをAppClassloaderの親ローダーとして使用します(これは継承関係ではないことに注意してください)。
  2. ここで確認できるのは、BootstrapClassloaderがExtClassloaderの親ローダーとして明示的に使用されていないことです。ただし、クラスローダーのloadClassメソッドから、ネイティブメソッドが呼び出されることがわかります。ネイティブメソッドは、実際にはBootstrapClassloaderから取得されます(BootstrapClassloaderは実際にはC ++で実装されており、Classloaderを継承しないため)
  3. スレッドのデフォルトのクラスローダーをAppClassloaderに設定します
  4. BootstrapClassloaderはここでは初期化されませんが、一部のパッケージをロードするためのSystem.getProperty( "sun.boot.class.path")のようなコードが存在する可能性があります。

System.getProperty( "sum.boot.class.path")の内容を出力すると、bootstrapClassloaderの内容を確認できます。また、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
复制代码

ここで初期化されたBootClassPathHolderもあります

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);
    }
}
复制代码

これが私の分析です:

  1. BootClassPath(つまり、System.getProperty( "sum.boot.class.path"))の場合、エグゼキュータはこれらのファイルを読み取り、File[]としてロードします。
  2. file []をトラバースしてパスかどうかを判断し、そうでない場合は、上位レベルのパスを取得します(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文件
    • 查找类是否存在

おすすめ

転載: juejin.im/post/7102054960933634084