类加载器介绍:
从虚拟机的角度说,只存在两种类加载器:
- 一种是启动类加载器,这个类加载器使用C++编写,是虚拟机自身的一部分;
- 另一种是所有其他类的加载器,这些类加载器使用Java编写,独立于虚拟机,并且全部继承自抽象类java.lang.ClassLoader;
从Java开发人员的角度看,类加载器可以细分为三种系统提供的类加载器:
- 启动类加载器Bootstrap ClassLoader:这类加载器负责加Java_homelib目录中的或者-XBootclasspath路径指定的,并且是虚拟机识别的类库加载到虚拟机内存中。
- 扩展类加载器Extension ClassLoader:这个加载器负责加载Java_homelibext目录中的或者有java.ext.dirs系统变量指定的路径下的类库,开发者可以直接使用扩展类加载器。
- 应用程序类加载器Application ClassLoader:一般称为系统类加载器,它负责加载用户类路径上所指定的类库,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下都是使用应用程序类加载器;
双亲委派模型
类加载器的双亲委托模型是JDK1.2被引入的。双亲委派模型不是一个强制性的约束模型,而是Java设计者推荐给开发者的一种类加载器实现方式。
如果上图所示的类加载器这种层次关系,称为类加载器的双亲委派模型。
双亲委派模型要求除了顶层的“启动类加载器”外,其他的类加载器必须有自己的父类加载器。(这里所说的子类与父类,一般不是由继承方式实现的,而是通过组合关系来复用父加载器的代码)
双亲委派模型的工作原理:
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给负责加载器去完成,每一层类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只由当父加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。
双亲委派模型优点:Java类随着它的类加载器一起具备了一种带有优先级的层次关系,例如类java.lang.Object它存在在rt.jar之中,无论哪个类加载器要加载这个类,最终都会委派给处于模型顶层的启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果没有使用双亲委派模型,由各个类加载器自行去加载的话,如果用户自己编写一个java.lang.Object类并放在ClassPath中,那么系统中将会存在多个不同的Object类。
双亲委派模式的具体实现在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) { //如果父加载器存在,则调用父加载器 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) { // 如果父加载器或者启动类加载器无法加载的时候,再调用本身的findClass方法来进行类加载 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; }