共计 3543 个字符,预计需要花费 9 分钟才能阅读完成。
回顾一下类加载过程
类加载过程:加载 -> 连贯 -> 初始化 。连贯过程又可分为三步: 验证 -> 筹备 -> 解析。
一个非数组类的加载阶段(加载阶段获取类的二进制字节流的动作)是可控性最强的阶段,这一步咱们能够去实现还能够自定义类加载器去管制字节流的获取形式(重写一个类加载器的 loadClass()
办法)。数组类型不通过类加载器创立,它由 Java 虚拟机间接创立。
所有的类都由类加载器加载,加载的作用就是将 .class 文件加载到内存。
类加载器总结
JVM 中内置了三个重要的 ClassLoader,除了 BootstrapClassLoader 其余类加载器均由 Java 实现且全副继承自java.lang.ClassLoader
:
- BootstrapClassLoader(启动类加载器):最顶层的加载类,由 C ++ 实现,负责加载
%JAVA_HOME%/lib
目录下的 jar 包和类或者或被-Xbootclasspath
参数指定的门路中的所有类。 - ExtensionClassLoader(扩大类加载器):次要负责加载目录
%JRE_HOME%/lib/ext
目录下的 jar 包和类,或被java.ext.dirs
零碎变量所指定的门路下的 jar 包。 - AppClassLoader(应用程序类加载器) : 面向咱们用户的加载器,负责加载以后利用 classpath 下的所有 jar 包和类。
双亲委派模型
双亲委派模型介绍
每一个类都有一个对应它的类加载器。零碎中的 ClassLoder 在协同工作的时候会默认应用 双亲委派模型。即在类加载的时候,零碎会首先判断以后类是否被加载过。曾经被加载的类会间接返回,否则才会尝试加载。加载的时候,首先会把该申请委派该父类加载器的 loadClass()
解决,因而所有的申请最终都应该传送到顶层的启动类加载器 BootstrapClassLoader
中。当父类加载器无奈解决时,才由本人来解决。当父类加载器为 null 时,会应用启动类加载器 BootstrapClassLoader
作为父类加载器。
每个类加载都有一个父类加载器,咱们通过上面的程序来验证。
`public class ClassLoaderDemo {public static void main(String[] args) {System.out.println("ClassLodarDemo's ClassLoader is " + ClassLoaderDemo.class.getClassLoader());
System.out.println("The Parent of ClassLodarDemo's ClassLoader is " + ClassLoaderDemo.class.getClassLoader().getParent());
System.out.println("The GrandParent of ClassLodarDemo's ClassLoader is " + ClassLoaderDemo.class.getClassLoader().getParent().getParent());
}
}`
Output
`ClassLodarDemo's ClassLoader is sun.misc.Launcher$AppClassLoader@18b4aac2
The Parent of ClassLodarDemo's ClassLoader is sun.misc.Launcher$ExtClassLoader@1b6d3586
The GrandParent of ClassLodarDemo's ClassLoader is null`
AppClassLoader
的父类加载器为 ExtClassLoader
ExtClassLoader
的父类加载器为 null,null 并不代表 ExtClassLoader
没有父类加载器,而是 BootstrapClassLoader
。
其实这个双亲翻译的容易让他人误会,咱们个别了解的双亲都是父母,这里的双亲更多地表白的是“父母这一辈”的人而已,并不是说真的有一个 Mother ClassLoader 和一个 Father ClassLoader。另外,类加载器之间的“父子”关系也不是通过继承来体现的,是由“优先级”来决定。官网 API 文档对这部分的形容如下:
The Java platform uses a delegation model for loading classes. The basic idea is that every class loader has a“parent”class loader. When loading a class, a class loader first“delegates”the search for the class to its parent class loader before attempting to find the class itself.
双亲委派模型实现源码剖析
双亲委派模型的实现代码非常简单,逻辑十分清晰,都集中在 java.lang.ClassLoader
的 loadClass()
中,相干代码如下所示。
`private final ClassLoader parent;
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) {// 父加载器不为空,调用父加载器 loadClass()办法解决
c = parent.loadClass(name, false);
} else {// 父加载器为空,应用启动类加载器 BootstrapClassLoader 加载
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {// 抛出异样阐明父类加载器无奈实现加载申请}
if (c == null) {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 程序的稳固运行,能够防止类的反复加载(JVM 辨别不同类的形式不仅仅依据类名,雷同的类文件被不同的类加载器加载产生的是两个不同的类),也保障了 Java 的外围 API 不被篡改。如果没有应用双亲委派模型,而是每个类加载器加载本人的话就会呈现一些问题,比方咱们编写一个称为 java.lang.Object
类的话,那么程序运行的时候,零碎就会呈现多个不同的 Object
类。
如果咱们不想用双亲委派模型怎么办?
为了防止双亲委托机制,咱们能够本人定义一个类加载器,而后重写 loadClass()
即可。
欠缺修改(issue871:类加载器一问的补充阐明):
自定义加载器的话,须要继承 ClassLoader
。如果咱们不想突破双亲委派模型,就重写 ClassLoader
类中的 findClass()
办法即可,无奈被父类加载器加载的类最终会通过这个办法被加载。然而,如果想突破双亲委派模型则须要重写 loadClass()
办法
自定义类加载器
除了 BootstrapClassLoader
其余类加载器均由 Java 实现且全副继承自java.lang.ClassLoader
。如果咱们要自定义本人的类加载器,很显著须要继承 ClassLoader
。
举荐浏览
- 深入分析 Java ClassLoader 原理
- 《2020 最新 Java 根底精讲视频教程和学习路线!》
- 老大难的 Java ClassLoader 再不了解就老了
- Java 类加载器(ClassLoader)
作者:Snailclimb
链接:类加载器
起源:github