什么是ClassLoader
写好的java程序,先被编译成class文件,然后才能被执行;类加载机制(ClassLoader)提了供动态加载class文件到jvm内存的功能。
类加载过程
-
加载,指虚拟机查找class文件,并创建class对象的过程
-
校验,保证class文件的字节流信息符合JVM规范
文件格式验证:验证字节流文件是否符合Class文件格式的规范,并且能被当前虚拟机正确的处理。元数据验证:是对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言的规范要求
字节码验证:主要是进行数据流和控制流的分析,保证被 校验类的方法在运行时不会危害虚拟机。
符号引用验证:符号引用验证发生在虚拟机将符号引用转化为直接引用的时候,这个转化动作将在解析阶段中发生。
-
准备,分配内存并设置变量的初始化值;
-
解析,将常量池内的符号引用替换成直接引用
-
初始化,静态变量的赋值和静态代码量的执行
java默认的类加载器
- 启动类加载器 BootStrap classLoader,Java类加载层次中最顶层的类加载器,加载JDK核心类库
- 扩展类加载器 Extonsion ClassLoader,负责加载java扩展类库
- 应用程序类加载器 App ClassLoader,负责加载classpath 目录下所有jar和class文件
加载原理-双亲委托模型
每个ClassLoader实例都有一个父类加载器的引用(不是继承的关系,是一个包含的关系),虚拟机内置的类加载器(Bootstrap ClassLoader)本身没有父类加载器,但可以用作其它ClassLoader实例的的父类加载器。当一个ClassLoader实例需要加载某个类时,它会试图亲自搜索某个类之前,先把这个任务委托给它的父类加载器,这个过程是由上至下依次检查的,首先由最顶层的类加载器Bootstrap ClassLoader试图加载,如果没加载到,则把任务转交给Extension ClassLoader试图加载,如果也没加载到,则转交给App ClassLoader 进行加载,如果它也没有加载得到的话,则返回给委托的发起者,由它到指定的文件系统或网络等URL中加载该类。如果它们都没有加载到这个类时,则抛出ClassNotFoundException异常。否则将这个找到的类生成一个类的定义,并将它加载到内存当中,最后返回这个类在内存中的Class实例对象。
双亲委派模型避免了类的重复加载
双亲委派模型的验证
package org.example;
class MyClassLoader extends ClassLoader {
private String name;
public MyClassLoader(String name) {
this.name = name;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] data = getClassData(name);
return defineClass(name, data, 0, data.length);
}
private byte[] getClassData(String className) {
// 省略获取类字节码的过程
return null;
}
}
package org.example;
/**
* @author xwg
* @date 2023/4/6 22:34
*/
public class ClassLoaderDemo {
public static void main(String[] args) {
ClassLoader classLoader = ClassLoaderDemo.class.getClassLoader();
System.out.println("ClassLoaderDemo的类加载器:"+ classLoader);
// 获取系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println("系统类加载器:" + systemClassLoader);
// 获取系统类加载器的父类加载器,即扩展类加载器
ClassLoader extensionClassLoader = systemClassLoader.getParent();
System.out.println("扩展类加载器:" + extensionClassLoader);
// 获取扩展类加载器的父类加载器,即启动类加载器,因为Bootstrap ClassLoader不是一个普通的Java类,返回为null
ClassLoader bootstrapClassLoader = extensionClassLoader.getParent();
System.out.println("启动类加载器:" + bootstrapClassLoader);
// 尝试使用自定义类加载器加载系统类库中的类
try {
MyClassLoader myClassLoader = new MyClassLoader("myClassLoader");
Class<?> clazz = myClassLoader.loadClass("java.lang.String");
System.out.println("自定义类加载器加载String类的结果:" + clazz.getClassLoader());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
ClassLoader 常用API
-
ClassLoader类是一个抽象类 ApplicationClassLoader和ExtensionClassLoader具体实现类是在sun.misc.launcher类中,BootStrapClassLoader不是java实现的;
-
每个class对象都有一个方法可以获取加载他的ClassLoader
public ClassLoader getClassLoader()
-
Classloader 常用方法
//获取父ClassLoader public final ClassLoader getParent() //获取系统类加载器,静态方法 public static ClassLoader getSystemClassLoader() //加载类 public Class<?> loadClass(String name) throws ClassNotFoundException //查找已加载的类 protected final Class<?> findLoadedClass(String name) //defineClass 此方法负责将二进制字节流转换为Class对象 protected final Class<?> defineClass(String name, byte[] b, int off, int len,ProtectionDomain protectionDomain) throws ClassFormatError