「深入理解 JVM 一」类加载器及自定义类加载器
一、 类加载器介绍
类加载器负责在运行期间将 Java 类动态加载到 JVM 内存中。因此 JVM 不需要了解底层文件或者文件系统来运行 Java 程序。类经过: 加载、链接(验证、准备、解析)、初始化,最终形成可以被虚拟机直接使用的 Java 类型。
-
加载:将 .class 文件加载到内存中
-
链接:
- 验证:验证 class 文件的正确性
- 准备:给类的静态变量分配内存,并且赋默认值。
- 解析:将符号引用变为直接引用 ,类加载的 resolve() 方法。相当于将另一个被引用的类的方法或者成员变量解析为直接定位到该方法或者成员变量的地址,访问的目标被加载到内存中。
-
初始化:对类的静态变量赋值
二、类加载器的种类
2.1 根加载器 (BootStrap ClassLoader)
主要加载 JDK 的内部类,主要是 rt.jar 以及其他在 */jre/lib 下的核心类。根加载器是所有加载器的父加载器。
2.2 扩展类加载器 (Extension ClassLoader)
是根加载器的子加载器,负责 $JAVA_HOME/lib/ext 目录下或者 java.ext.dirs 系统变量指定路径下扩展类的加载。
2.3 系统类加载器 (Application ClassLoader)
也叫做应用程序类加载器。是扩展类加载器的子加载器。负责加载用户类路径 classpath 指定的类。
三、类加载的过程
当 JVM 需要一个类时,类加载器收到类加载请求。首先从下往上检索该类是否被加载过,如果被加载过那么直接返回。否则,该加载器先递归地委托给父加载器加载,效果也就是自顶向下加载类,如果父加载器不能加载类那么就交给子加载器加载。如果父加载器加载失败,那么抛出 ClassNotFoundException 异常,再调用自己的 findClass() 方法进行加载。
3.1 双亲委派模型 (delegation)
以上过程就是双亲委派的过程。
为什么使用双亲委派模型呢?
- 提高安全性
- 为了保护系统核心类不被篡改。如果用户编写了一个 java.lang.Object 这种核心类,功能和系统 Object 类相同,但是植入了恶意代码。有了双亲委派模型,自定义的 Object 类是不会被加载的,因为根加载器会首先加载系统 Object 类,而不会加载自定义的 Object 类。
- 防止程序混乱
- 首先明确,jvm判定两个对象同属于一个类型:同名类实例化,实例对应的同名类的加载器必须相同。
- 要是每个加载器都自己加载的话,那么可能会出现多个 Object 类,导致混乱。
四、自定义类加载器
4.1 自定义类加载器的实现
自定义类加载器需要继承 ClassLoader 类,然后重写 findClass() 方法 或者重写 loadClass() 方法(如果需要打破双亲委派模型)
下面我们重写 findClass() 方法:
import java.io.*;
public class MyTest16 extends ClassLoader{
private String classLoaderName;
private String extensionName = ".class";
public MyTest16(String classLoaderName) {
super();
this.classLoaderName = classLoaderName;
}
public MyTest16(ClassLoader parent, String classLoaderName) {
super(parent);
this.classLoaderName = classLoaderName;
}
private byte[] loadClassData(String name){
FileInputStream fis = null;
ByteArrayOutputStream bos = null;
byte[] data = null;
try {
name = name.replace('.'