揭秘ClassLoader

Java类加载器(classloader)的功能就是加载类的描述对象Class到内存中,可以加载本地的.class文件也可以加载网络上的数据,本质上做的事情就是由java字节码到java.lang.Class对象的过程。
类加载器一般情况下有三种:
第一种:Bootstrap ClassLoader。它 负责加载java基础类,主要是 %JRE_HOME/lib/ 目录下(或者通过参数-Xbootclasspath指定)的rt.jar、resources.jar、charsets.jar和class等。
第二种:Extension ClassLoader。 负责加载java扩展类,主要是 %JRE_HOME/lib/ext 目录下的jar和class(或者通过参数-Djava.ext.dirs指定)。
第三种:App ClassLoader(也叫System ClassLoader)。AppClassLoader会加载java环境变量CLASSPATH所指定的路径下的类库,而CLASSPATH所指定的路径可以通过System.getProperty("java.class.path")获取;当然,该变量也可以覆盖,可以使用参数-cp,例如:java -cp 路径 (可以指定要执行的class目录)。
对于App ClassLoader与Extension ClassLoader都是sun.misc.Launcher里的一个内部类,但是BootStrapClassLoader是一个由C++编写的类,涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不能直接通过引用进行操作。
首先我么看一下作为App ClassLoader与Extension ClassLoader基类是如何实例化的。

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.ExtClassLoader.getExtClassLoader()获取ExtClassLoader,里面做的事情也就是初始化一个Launcher.ExtClassLoader的实例。new Launcher.ExtClassLoader(var0)其中这个var0就是ExtClassLoader要处理的路径中的文件。
  1. Launcher.AppClassLoader.getAppClassLoader(var1),其中这个var1,就是上面提到的ExtClassLoader,这个方法所做的事情,就是根据传入的ExtClassLoader以及AppClassLoader要处理的路径中的File
  2. 设置当前线程的classload为AppClassLoader。
  3. 如果应用设置了系统参数--安全管理器,会调用System.setSecurityManager设置进去。

既然我们谈到了类加载器就不得不说他是如何加载的。

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) {
                        //调用parent处理加载请求,递归了
                        c = parent.loadClass(name, false);
                    } else {
                        //没有parent说明是bootstrap
                        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;
        }
    }

ClassLoader中的双亲委派机制:

  • 当AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。

  • 当ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。

  • 如果BootStrapClassLoader加载失败会使用ExtClassLoader来尝试加载;

  • 若ExtClassLoader也加载失败,则会使用AppClassLoader来加载,如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException。

双亲委派的好处:安全,因为ClassLoader加载的class文件来源很多,比如编译器编译生成的class、或者网络下载的字节码,而一些来源的class文件是不可靠的,比如说手动创建了一个java.lang.Integer 的类与jdk自带的相冲突,利用双亲委派的机制,逐级委托,最终会被bootstrapClassLoader所加载,加载的是jdk自带,不会加载自定义的,这样就保证了安全性。

参考文献:java安全沙箱(一)之ClassLoader双亲委派机制

你可能感兴趣的:(揭秘ClassLoader)