Java类加载器

Chiexf Lv4

0x00 什么是类加载器

在Java中,类加载器(ClassLoader)是负责将.class文件或其他形式的字节码数据加载到Java虚拟机(JVM)中,并定义生成相应类的方法。Java的类加载器遵循双亲委派模型,它负责从不同的来源加载类,包括本地文件系统、网络等。

0x01 类加载器的作用

将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口

0x02 什么是类缓存

标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。

0x03 类的加载器类型

引导类加载器

Bootstrap Class Loader,也称为根加载器,负责加载Java的核心类库,如rt.jar等。它是虚拟机的一部分,由C++实现,并不是一个Java类。该加载器无法直接获取。

扩展类加载器

Extension Class Loader,负责加载Java扩展库,位于<JAVA_HOME>/lib/ext目录下的jar包。它是由Java编写的。

应用类加载器

Application Class Loader,也称为系统类加载器,负责加载应用程序的类。它是ClassLoader类的子类,并由Java编写。也是最常用的加载器。

获取类加载器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class TestClassLoader {
public static void main(String[] args) throws Exception {
//获取系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);

//获取应用类加载器的父类加载器--->扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);

//获取扩展类加载器父类加载器--->根加载器
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);

//测试当前类是哪个加载器加载的
ClassLoader classLoader = Class.forName("org.example.classloader.TestClassLoader").getClassLoader();
System.out.println(classLoader);

//测试JDK内置的类是谁加载的
//内置的是引导类加载器
classLoader = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader);

//获得系统类加载器可以加载的路径
// System.out.println(System.getProperty("java.class.path"));
}
}
  • 输出
1
2
3
4
5
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@677327b6
null
sun.misc.Launcher$AppClassLoader@18b4aac2
null

0x04 双亲委派机制

当一个类加载器收到加载请求时,它首先将加载任务委派给其父类加载器进行尝试。只有当父加载器无法加载时,才由当前类加载器

自己来尝试加载。这种层次结构保证了类的安全性和隔离性。如果自定义类加载器想要打破这个模型,可以覆盖加载逻辑。

特点:

  • 每个 class 都有对应的 ClassLoader
  • 除了 Bootstrap ClassLoader,每个 ClassLoader 都有一个 Parent ClassLoader
  • 对于一个类加载的请求,总是先委派给 Parent ClassLoader 来进行加载
  • 对于用户自定义的类加载器,默认的 Parent ClassLoader 是 AppClassLoader

0x05 类的加载过程

  • 加载

将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象。

  • 链接

将Java类的二进制代码合并到JVM的运行状态之中的过程。

​ 验证:

​ 确保加载的类信息符合JVM规范,没有安全方面的问题。

​ 准备:

​ 正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。

​ 解析:

​ 虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。

  • 初始化

执行类构造器()方法的过程。类构造器()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)

当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。

虚拟机会保证一个类的()方法在多线程 环境中被正确加锁和同步。