1. ClassLoader的继承关系
ClassLoader是什么鬼?为什么我们要如此大费周章的讲解这个?
还记得AppClassLoader、ExtClassLoader么?他们与ClassLoader之间的关系是什么?
ClassLoader继承关系 AppClassLoader+ExtClassLoader
URLClassLoader
SecureClassLoader
ClassLoader
2. ClassLoader重要方法loadclass()代码解读。
直接上代码,代码上注释有说明。
```
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected Class<?> loadClass(String name, boolean resolve) //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) { //如果父类存在
// 如果未加载过,则委派给父类进行加载。
c = parent.loadClass(name, false);
} else {
// 如果父类不存在,则交给BootstrapClassLoader来加载。 什么时候父类不存在呢?其实就是ExtClassLoader不存在父类的情况。
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
// 如果父类通过缓存+加载都无法找到,并抛出ClassNotFoundException异常时,则捕获异常但不处理。
}
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;
}
}
```
代码中有几个关键调用需要注意:
① Class<?> c = findLoadedClass(name)
通过缓存查找判断是否存在该类。
进一步查看该方法实现,又调用了native findLoadedClass0方法。
```
protected final Class<?> findLoadedClass(String name) {
if (!checkName(name))
return null;
return findLoadedClass0(name);
}
private native final Class<?> findLoadedClass0(String name);
```
② 当parent != null时,c = parent.loadClass(name, false);
。如果父类不为空,则委派给父类的loadClass()方法执行。
当 parent == null是,```c = findBootstrapClassOrNull(name);```父类如果为空时,则委派给BootstrapClassLoader来查找。
这里就是双亲委派模型出现了。
③ 当在经过父类们缓存查找和加载后,仍然未找到该类,则本加载器会亲自进行查找c = findClass(name);
。这个方法很关键。
```
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
```
3. 双亲委派模型的验证
public static void main(String[] args) {
ClassLoader loader = TestStatic3.class.getClassLoader();
System.out.println(loader);
System.out.println(loader.getParent());
System.out.println(loader.getParent().getParent());
}
输出结果:
sun.misc.Launcher$AppClassLoader@b4aac2
sun.misc.Launcher$ExtClassLoader@193b845
null
4. 双亲委派模型的优点
这里补充下几个双亲委派模型的特点。
- 系统类防止内存中出现多份同样的字节码
因为Java类随着它的类加载器一起具备了一种带有优先级的层次关系。双亲委派模型很好的解决了各个类加载器的基础类的统一问题(越基础的类由越上层的加载器进行加载)。 - 保证Java程序安全稳定运行
使用双亲委派模型来组织类加载器之间的关系,有一个显而易见的好处就是Java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如,类java.lang.Object,它存放在rt.jar中,无论哪一个类加载器要加载这个了类,最终都是委派给模型最顶端的启动类加载器进行加载,因此Object类在程序的各个类加载器环境中都是同一个类。
相反,如果没有使用双亲委派模型,由各个类加载器自行去加载的话,如果用户自己编写了一个称为java.lang.Object的类,并放在程序的ClassPath中,那系统中将会出现多个不同的Object类,Java类型体系中最基本的行为也就无法保证,应用程序也会变得一片混乱。
网友评论