Appclassloader是何时自己加载类(findclass)的?

526 阅读2分钟

前言

今天看Appclassloader( github.com/frohoff/jdk… ),发现一个自己以前没注意过的知识盲区,想了一会儿没想明白,最后恍然大悟:我是伞兵

正文

天下人都说双亲委派,委托父类加载器加载,父类加载器加载不到就自己加载,看这个代码,就很简单易懂嘛。
ClassLoader类

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

同时我们也知道loadclass就是双亲委派的逻辑所在

再看看Appclassloader的代码

/**
         * Override loadClass so we can checkPackageAccess.
         */
        public Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
        {
            int i = name.lastIndexOf('.');
            if (i != -1) {
                SecurityManager sm = System.getSecurityManager();
                if (sm != null) {
                    sm.checkPackageAccess(name.substring(0, i));
                }
            }

            if (ucp.knownToNotExist(name)) {
                // The class of the given name is not found in the parent
                // class loader as well as its local URLClassPath.
                // Check if this class has already been defined dynamically;
                // if so, return the loaded class; otherwise, skip the parent
                // delegation and findClass.
                Class<?> c = findLoadedClass(name);
                if (c != null) {
                    if (resolve) {
                        resolveClass(c);
                    }
                    return c;
                }
                throw new ClassNotFoundException(name);
            }

            return (super.loadClass(name, resolve));
        }

恩,看一下,AppClassLoader先去找自己有没有加载过该类,加载过的话就解析返回,没加载过的话就委托父类加载器加载。(致命问题)
再想想双亲委派的细节,父类加载器没有加载到就自己加载,AppclassLoader里好像只委托了父类,父类加载不到的话好像没有自己加载啊?
但是很明显我们随便写个类,psvm一下,他就是appclassloader加载的啊? 那appclassloader到底是何时加载的类?

解释

中间的搜索、debug过程就不赘述了,网上都只会复读机:Extclassloader加载不了就appclassloader自己加载,但是我就是找不到appclassloader何时加载的啊。。。心中大为不爽。。debug时发现,断点在appclassloader的return (super.loadClass(name, resolve));处,点击step into,进入了ClassLoader的loadClass方法,刚开始还没反应过来,心想这怎么回事,后来想通了恍然大悟,大呼我是伞兵

根源在于super.loadClass中的super,我把他当作了extclassloader!而其实他是父类的方法,根据appclassloader一路往上找,urlclassloader,SecureClassLoader,一路到了ClassLoader才有loadclass方法,因此super.loadClass调用的是ClassLoader类的loadclass!之后就开始双亲委派那套逻辑了,最终走到了URLCLASSLOADER的FindClass(name)中真正的自己加载了类。

警醒

父类加载不是父类!尽管我自以为我非常清楚的认识到这一点,但是我看到super的时候,仍然把他当作了父类加载器!

结语

我是伞兵

很小的一个错误,但是可能以后有人和我一样想不通的,所以放上网络供搜索。