持续更新JVM相关知识,敬请关注:
Java虚拟机精髓专栏zhuanlan.zhihu.com
上一节说了下类加载器和类加载过程。这一节我们看下几种不同的类加载器。
JVM支持的类加载器有两类,分别是引导类加载器和自定义加载器。这里的自定义自定义加载器,不仅仅指用户自己实现的加载器,而是泛指所有继承ClassLoader这个抽象类的加载器。
作为JAVA程序员,起码要知道引导加载器、扩展加载器、系统加载器这三种,除此之外,还有很多用户自定义加载器,他们的分类关系见下图。
JAVA内部实际的继承关系如何呢?大家可以查看下Laucher类,会发现其中有两个内部类,ExtClassLoader和AppClassLoader,这两个就是扩展类加载器和系统类加载器。他们都继承自URLClassLoader,URLClassLoader再继承自SecureClassLoader,最终继承ClassLoader。下面我们来看一个代码实例:
首先我们通过ClassLoader提供的方法直接获取系统类加载器,会发现是AppClassLoader,然后我们再通过getParent方法,获取上层扩展类加载器,发现是ExtClassLoader,之后我们再想getParent获取引导类加载器,发现获取不到了,输出了null。
下面我们通过当前类的类对象,获取他的ClassLoader,输出的是AppClassLoader,说明当前类是由系统类加载器加载的,并且大家注意,这个系统类加载器同之前那个内存地址是一致的,有此可见,这个系统类加载器只会被加载一次。
最后我们再看下String类的加载器,发现输出的是null,这就说明了,String加载器是通过引导类加载器加载的。系统的核心类库,都是使用引导类加载的。
下面我们来具体说下这几种不同的加载器:
1、引导类加载器(Bootstrap ClassLoader)
引导类加载器是由C和C++语言实现的,集成在JVM内部,所有JAVA的核心类库(rt.jar、resources.jar、sun.boot.class.path等)通过它来加载的。引导类加载器不继承java.lang.ClassLoader也没有上层加载器。他是扩展类加载器和应用类加载器的父类。Java由于安全方面的考虑,引导类加载器,只能加载java、javax、sun为开头的java自身的类库。
我们通过代码虽然获取不到引导类加载器,但是我们可以查看下可以加载哪些类库:
import
可以看到输出结果如下:
/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/resources.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/rt.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/sunrsasign.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/jsse.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/jce.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/charsets.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/jfr.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/classes
2、扩展类加载器(Extension ClassLoader)
扩展类加载器,是JVM内部自带的加载器,Java语言编写,对应sun.misc.Launcher$ExtClassLoader这个内部类。间接继承自ClassLoader类,他是通过引导类加载器进行加载的。扩展类加载器负责加载java.ext.dirs所指定目录或jre/libb/ext中的类库,我们自己定义的jar放到这些路径下,就会被扩展类加载器所加载。
3、系统类加载器(System ClassLoader)
系统类加载器,也叫做应用程序加载器,也是由JAVA语言编写的,对应sun.misc.Launcher$AppClassLoader这个内部类。同样间接继承ClassLoader类,它是通过扩展类加载器加载的。系统类加载器负责加载classpath路径下或java.class.path属性下的指定的了类库。实际上,系统类加载器是程序中的默认加载器,我们平常所编写的绝大不部分类,默认都是由这个加载器所加载的。这个可以看上面的代码演示结果。代码中,我们可以通过ClassLoader提供的getSystemClassLoader()方法获得到这个类加载器的实例。
4、用户自定义类加载器(User Defined ClassLoader)
在某些场合下,我们使用上述三种类加载器,无法满足我们的使用需求,这是就需要由我们自己去自定义一些类加载器,当然,这个在一般应用场景下会比较少用,所以在这不做过多讲解,之后再去详细说。用户可以通过继承ClassLoader,jdk1.2以后,可以重写findClass方法来实现自定义,这里主要编写的逻辑是加载对应路径的类的二进制数组,然后调用defineClass()方法去生成传入字节数组所代表类的实例。这里面对二进制数组的特殊操作,就可以根据需求去做更改了。如果需求更加简洁,也可以直接继承URLClassLoader来实现。