jvm -- 白话类加载器

永远固定的类加载器

根类加载器 (C++编写 ,是根类加载器  ,是没有指定父 类 加载器类加载器父类

ExtClassLoader(扩展类加载器 ,默认没有指定 父类加载器 )

AppClassLoader(应用类加载器 ,指定类加载器没 ExtClassLoader)

一般情况下,我们说 的结构

AppClassLoader的 父类加载器 为 ExtClassLoader ,是构造函数中指定的,使用组合表示  的类加载器

 

对于ExtClassLoader,构造函数

public ExtClassLoader(File[] var1) throws IOException {
super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
}

其中,设置null为 父类加载器
但是我们通常认为其 父类加载器 为 根类加载器,是因为在 LoadClass时,或默认选择 父类加载器进行加载,而 对于父类为空的情况,使用 根类加载其进行加载(findBootstrapClassOrNull就是,根类加载器 也被称为BootstrapClassLoader)

 

 

 

getSystemClassloader (系统类加载器),再没有使用java.system.class.loader进行指定的时候,就是AppClassLoader ,参看  https://www.cnblogs.com/sxrtb/p/14611395.html

public static ClassLoader getSystemClassLoader() {
        initSystemClassLoader();
        if (scl == null) {
            return null;
        }
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkClassLoaderPermission(scl, Reflection.getCallerClass());
        }
        return scl;
    }
    private static synchronized void initSystemClassLoader() {
        if (!sclSet) {
            if (scl != null)
                throw new IllegalStateException("recursive invocation");
            sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
            if (l != null) {
                Throwable oops = null;
                scl = l.getClassLoader();
                try {
                    scl = AccessController.doPrivileged(
                        new SystemClassLoaderAction(scl));//要是没有java.system.class.loader设置,则AppClassLoader为系统类加载器
                } catch (PrivilegedActionException pae) {
                    oops = pae.getCause();
                    if (oops instanceof InvocationTargetException) {
                        oops = oops.getCause();
                    }
                }
                if (oops != null) {
                    if (oops instanceof Error) {
                        throw (Error) oops;
                    } else {
                        // wrap the exception
                        throw new Error(oops);
                    }
                }
            }
            sclSet = true;
        }
    }
class SystemClassLoaderAction
    implements PrivilegedExceptionAction<ClassLoader> {
    private ClassLoader parent;

    SystemClassLoaderAction(ClassLoader parent) {
        this.parent = parent;
    }

    public ClassLoader run() throws Exception {
        String cls = System.getProperty("java.system.class.loader");
        if (cls == null) {
            return parent; //由InitSystemClassLoader可知,若此处返回 , 则值为AppClassLoader
        }

        Constructor<?> ctor = Class.forName(cls, true, parent)
            .getDeclaredConstructor(new Class<?>[] { ClassLoader.class });
        ClassLoader sys = (ClassLoader) ctor.newInstance(
            new Object[] { parent }); // java.system.class.loader必须有一个接受一个ClassLoader参数的构造函数 ,将AppClassLoader传入进去
        Thread.currentThread().setContextClassLoader(sys);//设置线程上下文类加载器
        return sys;
    }
}

 

指定了 java.system.class.loader的情况下 ,一般设置 AppClassLoader为 其父类加载器 (也就是说,可以 不 设置 AppClassLoader 为 系统类加载器的 父类 加载器)

具体还是上面的代码,最有的的一段

 

Thread.currentThread().getContextClassLoader() (线程上下文类加载器) 

这个的默认设置有两处

 

 

 

 由于此处调用没办法调式(反正我是没有调试出来),我个人的理解是

在没有设置  java.system.class.loader 的情况下,AppClassLoader为 线程上下文类加载器

在设置  java.system.class.loader 的情况下,线程上下文类加载器 为 System.class.loader (系统类加载器)

 

自定义类加载器

若自定义类加载器设置父类加载器为null ,参考https://www.cnblogs.com/sxrtb/p/14594324.html  实际上还是还是可以访问rt.jar(由根类加载器加载的类,而由于命名空间限制,类加载器只能访问自己加载的类 及其 父类加载器加载的 类),则其实根 类加载器可以 理解为是它的 父类加载器  (同ExtClassLoader设置父类加载器为 null 的 情况相同)

若构造函数中 使用super(),本质调用

    protected ClassLoader() {//使用 系统类 加载器 创建ClassLoader
        this(checkCreateClassLoader(), getSystemClassLoader());
    }

即,设置SystemClassLoader为其 父类加载器

当然,此处可以根据 设计需要,将另外一个自定义类加载器 设置为 其 父类加载器

 

问,我们自己写的程序到底是由那个类加载器进行?如运行 CacheClassLoaderTest2 

  系统类加载器。(具体验证,将自定义类加载器设置称系统类加载器,将自定义类加载器的父加载器设置成null,让自定义类加载器来加载 CacheClassLoaderTest2 ,输出结果肯定是 自定义类加载器。注意:此时原本由AppClassLoader加载的,但此时就没有用AppClassLoader加载,而是自定义的系统类加载器加载的。所以,我们自己写的程序,实质 使用系统类加载器 进行加载)

public class CacheClassLoaderTest2 {
    public static void main(String[] args)    {
        System.out.println(CacheClassLoaderTest2.class.getClassLoader());
    }
}

 

posted on 2021-04-02 18:53  xingshouzhan  阅读(58)  评论(0编辑  收藏  举报

导航