前言
上一篇 Java虚拟机——类加载机制 说到整个类加载过程中除了加载(加载是类加载的一个阶段)阶段,用户可以自定义类加载器参与之外,其余的阶段均完全由虚拟机主导和控制,实际上已经明确了类加载器工作的阶段和主要作用。
把“根据类的全限定名获取定义这个类的二进制字节流”这个动作放到虚拟机外部实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块被称为“类加载器”。
对于任意一个类,都需要这个类的类加载器和这个类本身才能确定其在虚拟机内存中的唯一性。换句话说就是两个类相等的必要不充分条件就是它们是由同一个类加载器加载的。
分类
从虚拟机(HotSpot)的角度来说,类加载器分为两类:Bootstrap ClassLoader和其他类加载器。Bootstrap ClassLoader是C++实现的,是虚拟机本身的一部分。其他类加载器都是由Java语言实现的,独立于虚拟机之外。从Java开发者的角度来说,JDK提供了三种常见的类加载器:Bootstrap ClassLoader、Extension ClassLoader、 Application ClassLoader。
- Bootstrap ClassLoader:启动类加载器,这个类加载器负责将
%JAVA_HOME%\bin
目录,或者-Xbootclasspath
所指定的目录中能被虚拟机识别的类库加载到虚拟机内存中。 - Extension ClassLoader:扩展类加载器,负责将
%JAVA_HOME%\lib\ext
目录中的,或者被java.ext.dirs
系统变量所指定路径中的所以有类库。 - Application ClassLoader:应用程序加载器,也叫
System ClassLoader
。负责加载classpath所指定的类库
除了JDK提供的三种类加载器外,用户还可以自己定义类加载器。
双亲委派机制
各种类加载器的关系如图所示:
类加载器之间这种层次关系称为类加载器的双亲委派模型,双亲委派模型要求除了Bootstrap ClassLoader外,其余的类加载器必须有自己的父加载器。这里类加载器的父子关系一般不会以继承(Inheritance)的方式实现,而是通过组合(Composition)方式复用父类代码。双亲委派机制不是虚拟机强制规定,而是虚拟机对类加载器实现的一种规范。双亲委派机制的工作原理是:当类加载器收到加载类的请求时,先把请求传递给父类加载器,每个加载器均是如此,所以每次请求都会到达Bootstrap ClassLoader,只有当父类加载器无法完成该类的加载时,子类加载器才会尝试自己加载。这样做可以避免虚拟机内存中出现两份一样的字节码。举个例子,自定义一个java.lang.String
类:
package java.lang;
public class String {
public static void main(String[] args) {
System.out.println("java.lang.String");
}
}
可以看出自定义的java.lang.String
可以编译,但是不能运行(准确的说是不能被加载),因为JDK自带的java.lang.String
已经被Bootstrap ClassLoader加载过了。
双亲委派的实现代码定义在java.lang.ClassLoader
中:
protected Class<?> loadClass(String name, boolean 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) {
// 如果父类加载器不为null,调用父类加载器来加载该类
c = parent.loadClass(name, false);
} else {
// 如果父类加载器为null,调用Bootstrap ClassLoader
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
// 如果仍然没有加载该类,则执行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;
}
}
参考
《深入理解Java虚拟机》