首页 > 代码库 > 【学习札记-类加载器】

【学习札记-类加载器】


       个人学习整理,如有不足之处,请不吝指教。转载请注明:@CSU-Max


类加载器


简介

    类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class类的一个实例。每个这样的实例用来表示一个 Java 类。通过此实例的 newInstance()方法就可以创建出该类的一个对象。实际的情况可能更加复杂,比如 Java 字节代码可能是通过工具动态生成的,也可能是通过网络下载的。


类加载器的分类

    Java 中的类加载器的分类如下图,看图简洁明了:


    启动类加载器(bootstrap class loader):加载 Java 的核心库。比如位于<JAVA_HOME>/jre/lib 目录下的vm.jar,core.jar。

    扩展类加载器(extensions class loader):加载 Java 的扩展库。一般位于<JAVA_HOME>/jre/lib/ext 或者通过java.ext.dirs 这个系统属性指定的路径下的代码。这个类加载器是由sun.misc.Launcher$ExtClassLoader 实现的。

    系统类加载器(system class loader):根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。加载java.class.path(映射系统参数 CLASSPATH的值) 路径下面的代码,这个类加载器是由 sun.misc.Launcher$AppClassLoader 实现的。

类加载器的层次结构和双亲委派模型


分析

    除了启动类加载器,所有的类加载器对象都有一个可以作为其双亲的类加载器对象。

    通过 ClassLoader 类的 getParent 方法可以获取双亲类加载器对象。
    我们自定义的类加载器 Java 类继承自 ClassLoader 类,而 ClassLoader 类的构造方法中可以指定类加载器的双亲类加载器对象,所以我们可以在自定义的类加载器的构造方法中调用父类 ClassLoader  类的构造方法,指定双亲类加载器对象的值。

    若我们在自定义类加载器时没有指定双亲类加载器,则默认的双亲类加载器是系统类加载器。
    若当前类加载器对象的双亲类加载器是启动类加载器,则其 getParent 方法返回 null。

    下面是测试代码:
public void test()
    {
        ClassLoader cl = getClass().getClassLoader();
        System.out.println(cl.toString());
        if (cl != null)
        {
            cl = cl.getParent();
            System.out.println(cl.toString());
        }
    }

    测试结果:
sun.misc.Launcher$AppClassLoader@4d77c977       ---> 默认为系统类加载器
sun.misc.Launcher$ExtClassLoader@734bcb5c       ---> getParent方法得到扩展类加载器
                                               ---> getParent方法返回null

    通过以上的简单代码和输出结果可以很好的说明我们上面的说法。

    根据以上的分析,我们可以发现这种加载的过程形成了一种层次结构,这种层次结构可以用如下简图表示:
            

           没有自己定义的类加载器时                                      有自己定义的类加载器时       


概念

    一般应用程序都是由以上三种基本的类加载器加载的,当然,有时候也会使用我们自定义的类加载器。这些类加载器构成了一种层次结构,称为类加载器的双亲委派模型。
    所有的类加载器(除了启动类加载器)对象都有一个可以作为其双亲的类加载器对象,通过组合关系来复用双亲类加载器的方法。

原理

    当一个类加载器收到类加载请求时,它并不会第一时间自己去加载这个类,而是把该请求委派给双亲类加载器去完成,每个层次的类加载器都是如此(双亲类加载器再将请求委派给它的双亲类加载器),如果双亲类加载器可以完成类加载任务,就成功返回;只有当双亲类加载器无法完成该加载请求时(它的搜索范围内没有找到所需要的类),子类加载器才会自己去加载。(此处可以通俗的理解为递归)

实现

    关于虚拟机中默认的双亲委派机制,我们可以查看 java.lang.ClassLoader 类的 loadClass 方法,源码及分析如下:
protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 首先判断请求的类是否已经被加载过了
            Class c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                //如果没有被加载,接着就检查双亲类加载器是否存在
                try {
                    if (parent != null) {
                        //如果双亲类加载器存在,就调用其loadClass方法
                        c = parent.loadClass(name, false);
                    } else {
                        //如果不存在双亲类加载器,就检查是否使用启动类加载器加载该类
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    
                }

                if (c == null) {
                    long t1 = System.nanoTime();
                    //如果依旧不能被加载,则调用自身的findClass来进行类加载
                    c = findClass(name);
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }


    最近在看 java 虚拟机这一块,将自己的一些学习心得记录下来,分享给大家,多多指教。


                                                    ***************************************************************************

                                                    *  转载请注明出处:  @CSU-Max    http://blog.csdn.net/csu_max      *

                                                    ***************************************************************************