Java源码解析(6) —— ClassLoader(1)

ClassLoader(1) —— 类加载核心类

1.类加载核心类,关于类加载详见:Java类的加载。
2.ClassLoader作用:将类二进制数据加载为JVM中的Class对象
3.关于阅读ClassLoader源码之前,最好理解一些相关体系、结构及相关名词含义:JVM安全体系:双亲委派、命名空间、策略、保护域。

源码解析:

public abstract class ClassLoader {//ClassLoader是个抽象类

    private static native void registerNatives();
    static {
        registerNatives();//本地方法注册
    }
    private final ClassLoader parent;//父加载器

    /**
     * 封装了并行的可装载的类型的集合(static)
     * 维护一张表,里面指出哪些类加载器具有并发能力,注意区分于parallelLockMap
     */
    private static class ParallelLoaders {
        private ParallelLoaders() {}

        // 具有并发能力的ClassLoader集合表(static)
        private static final Set> loaderTypes =
            Collections.newSetFromMap(
                new WeakHashMap, Boolean>());
        static {
            synchronized (loaderTypes) { loaderTypes.add(ClassLoader.class);//默认ClassLoader具有并发能力,很显然如此
             }
        }

        /**
         * 若其所有父类是具有并发能力的,则其也是
         */
        static boolean register(Class c) {
            synchronized (loaderTypes) {
                if (loaderTypes.contains(c.getSuperclass())) {
                //检查父类是否是即可,是,加入,不是则返回false
                    loaderTypes.add(c);
                    return true;
                } else {
                    return false;
                }
            }
        }

        /**
         * 返回给定的ClassLoader是否具有并发能力
         */
        static boolean isRegistered(Class c) {
            synchronized (loaderTypes) {
                return loaderTypes.contains(c);
            }
        }
    }

    //具有并发能力的类加载器:锁对象map(并发时候加锁对象)
    //String是该加载器加载的类的className,Object是其锁对象
    //parallelLockMap是一些时候导致内存溢出的原因:大量的类被创建,不止增加Class对象,同样增加该map大小
    private final ConcurrentHashMap parallelLockMap;

    // 类加载器:包签名(包权限资格)列表
    private final Map  package2certs;

    private static final Certificate[] nocerts = new Certificate[0];
    //该加载器加载的类的集合
    private final Vector> classes = new Vector<>();

    //默认的保护域
    private final ProtectionDomain defaultDomain =
        new ProtectionDomain(new CodeSource(null, (Certificate[]) null),
                             null, this, null);

    //被该类加载加载的类的保护域
    private final Set domains;

    // 加载后,将该类Class对象加载到集合中
    void addClass(Class c) {
        classes.addElement(c);
    }

    //该加载器可加载的包
    private final HashMap packages = new HashMap<>();
    //若存在安全管理器,检查是否可创建该加载器
    //security.checkCreateClassLoader()若不可以,直接抛异常
    private static Void checkCreateClassLoader() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkCreateClassLoader();
        }
        return null;
    }
    //私有构造器,若具有安全管理器,且不允许创建该类加载器,则直接抛异常
    private ClassLoader(Void unused, ClassLoader parent) {
        this.parent = parent;
        if (ParallelLoaders.isRegistered(this.getClass())) {
        //若该类加载器具有并行能力,初始化相关数据,均为线性安全集合
            parallelLockMap = new ConcurrentHashMap<>();
            package2certs = new ConcurrentHashMap<>();
            domains =
                Collections.synchronizedSet(new HashSet());
            assertionLock = new Object();
        } else {
            //若该类加载器具有并行能力,初始化相关数据,均为非线性安全集合
            parallelLockMap = null;
            package2certs = new Hashtable<>();
            domains = new HashSet<>();
            assertionLock = this;
        }
    }

    //可指定父类构造器
    protected ClassLoader(ClassLoader parent) {
        this(checkCreateClassLoader(), parent);
    }

    //构造器,默认当前系统加载器为父加载器
    protected ClassLoader() {
        this(checkCreateClassLoader(), getSystemClassLoader());
    }

    // -- Class --

    //加载指定名称的类,详见loadClass(name, false)
    public Class loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }

    /**
     * 加载指定二进制数据(流)成Class对象
     * 加载顺序:
     *
     * 首先,findLoadedClass(String),检查该类是否已被加载,已被加载不再加载
     *
     * 其次,尝试使用父加载器(有父加载器一直向上,即为双亲委派)加载该类
     *
     * 最后,若加载不成功,尝试自身加载该类
     *
     * 如果,参数resolve=true,进行类解析,详见resolveClass()
     *
     * 这个方法是可以重写的,但不推荐,重写会破坏双亲委派,推荐重写findClass方法
     */
protected Class loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
    //注意下面,如果该类加载器支持并发加载,则可以并发地加载各类,否则只能同步加载不同类(这一步同样可以防止并发地加载出同一个类的不同Class对象)
    //详见下方getClassLoadingLock(name)
        synchronized (getClassLoadingLock(name)) {
            /**
             * 最先,会查找该类是否已经被加载过
             * 这里的查找是native方法实现,其实现逻辑是同一个全名称类+同
             * 一个加载器方为同一个类,方为该类已加载
             */
            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异常由最末加载器抛出
                }

                if (c == null) {
                    //父加载器无法加载,则由自己试图加载
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // 记录加载信息
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;//记住,这里是自下而上(加载器链)试图加载的,上面不行,接着
            //由下面加载器加载,直到最末
        }
    }
//获取加载类时候,对应类的并发锁对象
protected Object getClassLoadingLock(String className) {
        Object lock = this;
        if (parallelLockMap != null) {
        //若该加载器支持并发,则不同类具有不同的锁对象,若不存则新建,返回对象的锁对象
            Object newLock = new Object();
            lock = parallelLockMap.putIfAbsent(className, newLock);
            if (lock == null) {
                lock = newLock;
            }
        }
        //若不支持并发,则直接返回该加载器作为锁对象,即该加载器只能同步加载任何类
        return lock;
    }

分章总结:
1.JVM存在一张表loaderTypes,记录每一个类加载器是否具有并发加载能力
2.每一个类加载器,如果父类支持并发加载,其默认也具有
3.不具有并发能力,意思是这个加载器同时只能加载一个类;而具有的话,对于不同的类,这个加载器可以同时加载,当然同一个类是不能并发加载的,除非你重写loadClass(String name, boolean resolve)
4.每一个加载器保存着一张被其加载的类的map,parallelLockMap,key是类名,value是并发加载锁对象,当然,若不具有并发能力,parallelLockMap=null
5.双亲委派是由loadClass(String name, boolean resolve)方法实现,这个方法可以重写,重写了就破坏了双亲委派机制

你可能感兴趣的:(Java源码解析,Java源码解析)