双亲委派机制
JVM提供了三种类加载器,分别为启动类加载器(Bootstrap Classloader)、扩展类加载器(Extention Classloader)和应用程序类加载器(Application Classloader)。其中启动类加载器是使用C/C++语言实现的,是虚拟机自身的一部分;其它的类加载器,都是由Java语言实现的,独立存在于虚拟机外部,并且全部继承自抽象类java.lang.ClassLoader。
- 启动类加载器:虚拟机内置加类加载器,通常表示为null,没有父加载器。负责加载JAVA_HOME/lib目录中的类库,或通过-Xbootclasspath参数指定路径中被虚拟机认可的类库
- 扩展类加载器:是在类sun.misc.Launcher$ExtClassLoader中以Java代码的形式实现的。负责加载JAVA_HOME/lib/ext目录中的类库,或通过java.ext.dirs系统变量加载指定路径中的类库
- 应用程序类加载器:这个类加载器由sun.misc.Launcher$AppClassLoader来实现。由于应用程序类加载器是ClassLoader类中getSystem-ClassLoader()方法的返回值,所有有些场合中也成它为”系统类加载器”。负责加载用户路径classpath上面的类库。
除了JVM提供的三种默认的类加载器,用户也可以通过继承java.lang.ClassLoader实现自定义类加载器。
双亲委派
JVM通过双亲委派机制对类进行加载。双亲委派机制指一个类在收到类加载请求后,不会尝试自己加载这个类,而是把该类加载请求向上委派其父类去完成,其父类在接收到该类加载请求后又会将其委派个自己的父类,以此类推,这样所有的类加载请求都被向上委派到启动类加载器中。若父类加载器在接收到类加载请求后发现自己也无法加载该类(通常原因是该类的Class文件在父类的类加载路径中不存在),则父类会将该信息反馈给子类并向下委派子类加载器加载该类,知道该类被成功加载,若找不到该类,则JVM会抛出ClassNotFound异常。
双亲委派类加载机制的类加载流程如下
1、将自定义加载器挂载到应用程序类加载器
2、应用程序类加载器将类加载请求委托给扩展类加载器
3、扩展类加载器将类加载请求委托给启动类加载器
4、启动类加载器在加载路径下查找并加载Class文件,如果未找到目标Class文件,则交由扩展类加载器加载
5、扩展类加载器在加载路径下查找并加载Class文件,如果未找到目标Class文件,则交由应用程序类加载器加载
6、应用程序类加载器在加载路径下查找并加载Class文件,如果未找到目标Class文件,则交由自定义类加载器加载
7、在自定义类加载器下查找并加载用户指定目录下的Class文件,如果在自定义加载路径下未找到目标Class文件,则抛出ClassNotFound异常。
双亲委派机制的核心是保障类的唯一性和安全性。例如,在加载rt.jar包中的java.lang.Object类时,无论是哪个类加载器加载这个类,最终都将类加载请求委托给启动类加载器加载,这样就保证了类加载的唯一性。如果在JVM中存在包名和类名相同的两个类,则该类将无法被加载,JVM无法完成该类加载流程。
源码查看
ClassLoader类中loaderClass方法
/**
* 加载规范的二进制类名称
* 默认实现按照以下方式查找类
*
* <ol>
* <li><p> 调用findLoadedClass(String)方法检查该类是否已经被加载</p></li>
*
* <li><p> 在父类加载器上调用loadClass方法。
* 如果父级是null ,则使用内置到虚拟机中的类加载器。 </p></li>
*
* <li><p> 调用findClass(String)方法找到该类</p></li>
* </ol>
*
* <p> 如果类在上述步骤中找到, 且resolve标识为true
* 则该方法将调用resolveClass(Class)方法对所生成的Class对象.
*
* <p> 鼓励ClassLoader子类覆盖findClass(String) ,而不是这种方法</p>
*
* <p> 除非被覆盖,否则该方法在整个类加载过程中同步getClassLoadingLock方法的结果
*
* @param 类名称
* @param 装载标识
* @return 结果class类
* @throws 如果没有找到该类,抛出ClassNotFoundException异常
*
*/
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// 使用同步锁,加载类名称,避免多线程重复加载
synchronized (getClassLoadingLock(name)) {
// 第一步,检查该类是否已经类加载
Class<?> c = findLoadedClass(name);
// 如果未加载
if (c == null) {
long t0 = System.nanoTime();
try {
// 其父加载器不为null
// 则说明父加载器为应用程序类加载器或扩展类加载器
if (parent != null) {
// 交由其父加载器加载该class类
c = parent.loadClass(name, false);
} else {
// 若父加载器为null,则说明其父加载器为启动类加载器
// 交由启动类加载器加载该类
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// 捕获查找过程中的异常信息
}
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;
}
}
还没有评论,来说两句吧...