前文已经基本介绍类加载及类加载器,本文主要讨论其在JDK中具体完成机制

怎么确认class被哪个classLoader加载?

我们知道类加载是有双亲派遣机制的,具体基本如下图

Classloader源码解读

怎么加载类?

现在有三种方式:

  1. 命令行发动运用时分由JVM初始化加载 经过main()入口,加载一系列资源
  2. 经过Class.forName()办法动态加载
  3. 经过ClassLoader.loadClass()办法动态加载

代码如下:

public static void main(String args[]) throws ClassNotFoundException {
    Class cl = Class.forName("com.hyw.algorithm.BoyerMooreTest");
    System.out.println(cl);
    cl = ClassloaderTest.class.getClassLoader().loadClass("com.hyw.algorithm.BoyerMooreTest");
    System.out.println(cl);
    //获取的是appclassloader
    cl = ClassLoader.getSystemClassLoader().loadClass("com.hyw.algorithm.BoyerMooreTest");
    System.out.println(cl);
}

关注点Class.forName和ClassLoader().loadClass区别:

  • Class.forName(): 将类的.class文件加载到jvm中之外,还会对类进行解说,履行类中的static块;
  • ClassLoader.loadClass(): 只干一件事情,便是将.class文件加载到jvm中,不会履行static中的内容,只要在newInstance才会去履行static块。
  • Class.forName(name, initialize, loader)带参函数也可控制是否加载static块。并且只要调用了newInstance()办法采用调用构造函数,创建类的目标 。

双亲派遣机制源码

具体在ClassLoader.java

public Class<?> loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);
}
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;
    }
}

Bootstrap ClassLoader不是Ext ClassLoader的父类,假如父类为空,默许便是Bootstrap ClassLoader

怎么确定一个类被哪个classLoader加载?

能够用ClassloaderTest.class.getClassLoader()来判别

ClassLoader classLoader = ClassloaderTest.class.getClassLoader();
System.out.println(classLoader);
while (Objects.nonNull(classLoader.getParent())) {
    classLoader = classLoader.getParent();
    System.out.println(classLoader);
}

在idea中履行成果如下:

sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@7f31245a

一般在系统分析时,不太可能直接打印这个,能够依据如下来进行判别:

  • Bootstrap ClassLoader:担任加载存放在JDKjrelib(JDK代表JDK的安装目录,下同)下,或被-Xbootclasspath参数指定的途径中的,并且能被虚拟机辨认的类库(如rt.jar,一切的java.*最初的类均被Bootstrap ClassLoader加载)。发动类加载器是无法被Java程序直接引用的

发动参数环境变量 -Dsun.boot.class.path

  • Extension ClassLoader:由sun.misc.Launcher$ExtClassLoader完成,它担任加载JDKjrelibext目录中,或许由java.ext.dirs系统变量指定的途径中的一切类库(如javax.*最初的类),开发者能够直接运用扩展类加载器

发动参数 -Djava.ext.dirs=xxx

  • Application ClassLoader:该类加载器由sun.misc.Launcher$AppClassLoader来完成,它担任加载用户类途径(ClassPath)所指定的类,开发者能够直接运用该类加载器,假如运用程序中没有自定义过自己的类加载器,一般情况下这个便是程序中默许的类加载器。

发动参数 -cp或许-classpath -Djava.class.path

系统默许几个ClassLoader具体完成是啥?

具体完成在sun.misc.Launcher

Bootstrap ClassLoader

发动时默许加载的

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

Extension ClassLoader

static class ExtClassLoader extends URLClassLoader {
    private static volatile Launcher.ExtClassLoader instance;
    public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
        if (instance == null) {
            Class var0 = Launcher.ExtClassLoader.class;
            synchronized(Launcher.ExtClassLoader.class) {
                if (instance == null) {
                    instance = createExtClassLoader();
                }
            }
        }
        return instance;
    }
    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();
        }
    }
    void addExtURL(URL var1) {
        super.addURL(var1);
    }
    public ExtClassLoader(File[] var1) throws IOException {
        super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
        SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
    }
    private static File[] getExtDirs() {
        String var0 = System.getProperty("java.ext.dirs");
        File[] var1;
        if (var0 != null) {
            StringTokenizer var2 = new StringTokenizer(var0, File.pathSeparator);
            int var3 = var2.countTokens();
            var1 = new File[var3];
            for(int var4 = 0; var4 < var3;   var4) {
                var1[var4] = new File(var2.nextToken());
            }
        } else {
            var1 = new File[0];
        }
        return var1;
    }
    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;
    }
    public String findLibrary(String var1) {
        var1 = System.mapLibraryName(var1);
        URL[] var2 = super.getURLs();
        File var3 = null;
        for(int var4 = 0; var4 < var2.length;   var4) {
            URI var5;
            try {
                var5 = var2[var4].toURI();
            } catch (URISyntaxException var9) {
                continue;
            }
            File var6 = var5.getAuthority() == null ? (new File(var5)).getParentFile() : Paths.get(var5).toFile().getParentFile();
            if (var6 != null && !var6.equals(var3)) {
                String var7 = VM.getSavedProperty("os.arch");
                File var8;
                if (var7 != null) {
                    var8 = new File(new File(var6, var7), var1);
                    if (var8.exists()) {
                        return var8.getAbsolutePath();
                    }
                }
                var8 = new File(var6, var1);
                if (var8.exists()) {
                    return var8.getAbsolutePath();
                }
            }
            var3 = var6;
        }
        return null;
    }
    private static AccessControlContext getContext(File[] var0) throws IOException {
        PathPermissions var1 = new PathPermissions(var0);
        ProtectionDomain var2 = new ProtectionDomain(new CodeSource(var1.getCodeBase(), (Certificate[])null), var1);
        AccessControlContext var3 = new AccessControlContext(new ProtectionDomain[]{var2});
        return var3;
    }
    static {
        ClassLoader.registerAsParallelCapable();
        instance = null;
    }
}

Application ClassLoader

static class AppClassLoader extends URLClassLoader {
    final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);
    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);
            }
        });
    }
    AppClassLoader(URL[] var1, ClassLoader var2) {
        super(var1, var2, Launcher.factory);
        this.ucp.initLookupCache(this);
    }
    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);
        }
    }
    protected PermissionCollection getPermissions(CodeSource var1) {
        PermissionCollection var2 = super.getPermissions(var1);
        var2.add(new RuntimePermission("exitVM"));
        return var2;
    }
    private void appendToClassPathForInstrumentation(String var1) {
        assert Thread.holdsLock(this);
        super.addURL(Launcher.getFileURL(new File(var1)));
    }
    private static AccessControlContext getContext(File[] var0) throws MalformedURLException {
        PathPermissions var1 = new PathPermissions(var0);
        ProtectionDomain var2 = new ProtectionDomain(new CodeSource(var1.getCodeBase(), (Certificate[])null), var1);
        AccessControlContext var3 = new AccessControlContext(new ProtectionDomain[]{var2});
        return var3;
    }
    static {
        ClassLoader.registerAsParallelCapable();
    }
}

Laucher

上述几个classLoader在发动时分都会加载,默许给加载线程设置的classloader是APPClassLoader

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

ExtClassLoader和AppClassLoader都是继承URLClassLoader

参阅文档:
JVM 基础 – Java 类加载机制
一看你就懂,超具体java中的ClassLoader详解
ClassLoader资源加载机制