一,类加载器
虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称为“类加载器”。
好处:避免了类的重复加载,同时保护程序安全,防止核心API被随意篡改。
二,双亲委派模型
双亲委派模型的工作过程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
使用双亲委派模型来组织类加载器之间的关系,有一个显而易见的好处就是Java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存放在rt.jar之中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果没有使用双亲委派模型,由各个类加载器自行去加载的话,如果用户自己编写了一个称为java. lang.Object的类,并放在程序的ClassPath中,那系统中将会出现多个不同的Object类,Java类型体系中最基础的行为也就无法保证,应用程序也将会变得一片混乱。如果读者有兴趣的话,可以尝试去编写一个与rt.jar类库中已有类重名的Java类,将会发现可以正常编译,但永远无法被加载运行。
从Java虚拟机的角度来讲,只存在两种不同的类加载器:一种是启动类加载器(BootstrapClassLoader),这个类加载器使用C++语言实现,是虚拟机自身的一部分;另一种就是所有其他的类加载器,这些类加载器都由Java语言实现,独立于虚拟机外部,并且全都继承自抽象类 java.lang.ClassLoader。
2.1 APP类加载器 Bootstrap ClassLoader
这是 JVM 的 根 ClassLoader,它是用C++实现的,JVM 启动时初始化此 ClassLoader,主要加载 java 的核心类库,rt,jar,resource.jar; 可以通过 XbootclassPath 指定核心类库的路径。
2.2 扩展类加载器 Extension ClassLoader
JVM 用此 classloader 来加载扩展功能的一些 jar 包,如 java.ext.dirs 系统属性所指的的目录, 或从 JDK 的安装目录的 jre/lib/ext 子目录下加载类库。如果用户创建的 jar 放在此目录下,也会自动由扩展类加载器加载。
扩展类加载器由 Java语言实现,代码中 getParent() 获取父加载器为 null。
2.3应用类加载器 AppClassLoader/System ClassLoader
JVM 用此 classloader 来加载启动参数中指定的 Classpath(位置:target包下) 中的 jar 包以及目录,在 Sun JDK 中 ClassLoader 对应的类名为 AppClassLoader。
三,java 8 中 ClassLoad.loadclass() 源码
双亲委派模型对于保证Java程序的稳定运作很重要,但它的实现却非常简单,实现双亲委派的代码都集中在java.lang.ClassLoader的loadClass()方法之中,逻辑清晰易懂:先检查是否已经被加载过,若没有加载则调用父加载器的 loadClass() 方法,若父加载器为空则默认使用启动类加载器作为父加载器。如果父类加载失败,抛出 ClassNotFoundException 异常后,再调用自己的findClass()方法进行加载。
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) {
// 是一个递归调用,直至没有父类的类加载器(扩展类加载器的 parent = null)
c = parent.loadClass(name, false);
} else {
// 加载java核心类库的加载器
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.
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;
}
}
四,URLClassloader中的findclass方法源码
protected Class<?> findClass(final String name)
throws ClassNotFoundException
{
final Class<?> result;
try {
result = AccessController.doPrivileged(
new PrivilegedExceptionAction<Class<?>>() {
public Class<?> run() throws ClassNotFoundException {
//拼接成指定前缀的.class文件
String path = name.replace('.', '/').concat(".class");
//从classpath获取到.class文件
Resource res = ucp.getResource(path, false);
if (res != null) {
try {
//从路径中读取class文件字节流,并定义成类;todo bytebuffer
return defineClass(name, res);
} catch (IOException e) {
throw new ClassNotFoundException(name, e);
}
} else {
return null;
}
}
}, acc);
} catch (java.security.PrivilegedActionException pae) {
throw (ClassNotFoundException) pae.getException();
}
if (result == null) {
throw new ClassNotFoundException(name);
}
return result;
}
评论区