类加载过程
类加载过程
类加载过程为 7 个阶段:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)。其中,验证、准备和解析这三个阶段可以统称为连接(Linking)。
1. 加载(Loading)
- 加载类的字节码: 类加载器读取类文件(通常是
.class
文件)的字节码内容,并将其转换为方法区中的类对象。这个过程中,类的静态变量、方法、以及其他信息都被加载到内存中。 - 查找和加载: 加载器会按照一定的顺序查找类文件,包括从本地文件系统、JAR包、甚至网络等不同位置加载类。
2. 验证(Verification)
- 确保字节码的正确性和安全性: 验证阶段主要是为了确保被加载的类符合JVM的要求,防止恶意代码或错误代码破坏系统的安全和稳定。验证包括四个方面:
- 文件格式验证: 检查字节流是否符合Class文件格式的规范。
- 元数据验证: 检查类的元数据信息,如类、接口、字段、方法的类型和修饰符是否合法。
- 字节码验证: 确保程序代码不会做出危害虚拟机安全的操作,如非法数据类型转换。
- 符号引用验证: 确保类、字段、方法等符号引用是可用的且符合访问控制规则。
3. 准备(Preparation)
- 分配内存: 为类的静态变量分配内存,并初始化为默认值(例如数值类型为0,引用类型为null)。
- 常量池中的符号引用转化为直接引用: 在这个阶段,类的常量池中记录的符号引用将会被替换成直接引用。
4. 解析(Resolution)
- 解析符号引用为直接引用: 解析阶段是将常量池中的符号引用替换为直接引用的过程,如将一个方法调用符号引用解析为实际的方法对象。解析包括类或接口的解析、字段解析、方法解析等。
- 动态链接: 在这个过程中,可以通过lazy resolution(懒解析)机制,即在真正用到某个符号时才进行解析。
5. 初始化(Initialization)
- 初始化静态变量: 执行类构造器
<clinit>()
方法。<clinit>()
方法是由编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并产生的。 - 静态代码块的执行: 类中定义的静态初始化块也会在这个阶段被执行。静态初始化块和静态变量的初始化按照它们在类中出现的顺序依次执行。
类卸载过程
在Java虚拟机中,类的卸载是指将已经加载的类从内存中移除的过程。类卸载的主要目的是释放内存资源,通常与类加载器的生命周期密切相关。
类卸载的条件
- 没有任何实例: 类的所有实例对象都已被垃圾回收,即堆中不存在该类的任何实例。
- 类的Class对象不可达: 类的Class对象本身没有被任何引用持有,这意味着没有任何代码或数据在使用该类。
- 类加载器可以被回收: 加载该类的类加载器本身没有被任何引用持有,且没有其他类依赖于它。类加载器的实例没有被使用时,也会被垃圾回收。
满足这些条件后,JVM会认为该类和它的类加载器可以被卸载。
类卸载的过程
- 垃圾回收标记: 在垃圾回收过程中,JVM首先会标记所有不再使用的对象,包括类的实例、Class对象和类加载器。
- 判断是否满足卸载条件: 如果一个类满足上述条件,并且它的类加载器也满足卸载条件,那么JVM将会执行类卸载操作。
- 清理类加载器和类的元数据: JVM会清除该类加载器的内部数据结构,包括类的定义、常量池、方法表等。这个过程释放了用于类和类加载器的内存。
- 释放类相关资源: 卸载类时,JVM还会释放与该类相关的资源,包括静态变量、静态方法等占用的内存。这也意味着类的静态代码块和静态变量不会再次初始化。
- 垃圾回收: 最终,类加载器和类相关的所有对象(包括Class对象)都会被垃圾回收,彻底释放内存。
双亲委派机制
双亲委派机制(Parent Delegation Model)是Java类加载器的一种设计模式,它确保Java类加载的一致性和安全性。通过这种机制,Java虚拟机可以避免重复加载类,确保同一个类在整个应用中唯一加载一次。以下是双亲委派机制的详细解释: