类加载机制
- 类加载机制
- 类加载的过程
- 加载(Loading)
- 验证(Verification)
- 准备(Preparation)
- 解析(Resolution)
- 初始化(Initialization)
- 类加载器
- 启动类加载器
- 扩展类加载器
- 应用程序类加载器
- 双亲委派模型
类加载机制
JDK的编译器javac负责将java文件编译成class字节码文件
java负责启动java虚拟机,把字节码文件读取到java虚拟机,由虚拟机同一管理类和对象的信息,类加载就是从字节码文件到对象创建并使用的过程
类加载的过程
加载(Loading)
这是类加载过程的第一步,主要是通过类的全限定名来获取定义此类的二进制字节流。字节流可以从多种来源获取,比如从本地文件系统读取.class文件、从网络下载字节流、从jar文件中提取等
将字节流所代表的静态存储结构转换为方法区中的运行时数据结构。方法区是 JVM 内存区域之一,用于存储已被虚拟机加载的类信息、常量、静态变量等数据
在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。这个Class对象是非常重要的,通过它可以访问类的各种信息,如类的属性、方法等
- 获取字节流
- 数据结构转换
- 生成对应的Class对象
验证(Verification)
确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全
- 对字节流进行文件格式验证(如检查魔数、主次版本号等是否正确)
- 元数据验证(如检查类是否有正确的继承关系等)
- 字节码验证(检查指令是否合法等)
- 符号引用验证(检查符号引用是否能正确解析等)
如果一个类文件的字节码被恶意篡改,可能在验证阶段就会被检测出来。
准备(Preparation)
为类变量(即被static修饰的变量)分配内存并设置初始值
。这里的初始值是指数据类型的默认零值
- 整型:0
- 浮点型:0.0
- 布尔型:false
- 字符型:空字符
- 引用型(对象):null
解析(Resolution)
这是虚拟机将常量池内的符号引用替换为直接引用的过程
符号引用
:一组符号来描述所引用的目标
直接引用
:直接指向目标的指针、句柄等
为了方便在运行时能够直接调用某个方法,一个类中引用了另一个类的方法,在解析阶段就会把这个方法的符号引用转换为直接引用
初始化(Initialization)
执行类构造器方法的过程。方法是由编译器自动收集类中的所有静态变量的赋值动作和静态语句块(static {})中的语句合并产生的。在初始化阶段,会按照语句在源程序中的顺序依次执行这些静态变量赋值和静态语句块中的语句
例如,对于一个有静态变量和静态块的类:
class TEST{// 准备阶段,分配age的内存,由于没有初始值,将默认初始值为0private static final int age; static {// 初始化阶段age = 18;}
}
类加载器
(ClassLoader)
启动类加载器
(Bootstrap ClassLoader)
它是由 C++ 语言实现的,是 JVM 自身的一部分,主要负责加载存放在<JAVA_HOME>\lib目录下的,或者被-Xbootclasspath参数所指定的路径中的,并且是 Java 虚拟机识别的(如rt.jar)类库。它是最顶层的类加载器,加载的类是最基础的、对整个 Java 系统都非常重要的类,如java.lang.Object等基础类
扩展类加载器
(Extension ClassLoader)
它负责加载<JAVA_HOME>\lib\ext目录下的类库,或者被java.ext.dirs系统变量所指定路径中的类库。主要用于加载 Java 的扩展类库,例如一些 Java 的标准扩展功能的类
应用程序类加载器
(Application ClassLoader)
也称为系统类加载器,它负责加载用户类路径(classpath)上的所有类库,是最常用的类加载器。在编写 Java 应用程序时,自己编写的类一般都是由这个类加载器加载的。它可以通过ClassLoader.getSystemClassLoader()方法获取
双亲委派模型
(Parent - Delegation Model)
这是 Java 类加载器的一种层次关系模型。当一个类加载器收到类加载请求时,它首先不会自己去尝试加载这个类,而是把请求委派给父类加载器。如果父类加载器可以完成加载任务,就成功返回;如果父类加载器无法加载(例如它在自己负责的路径下找不到对应的类),子加载器才会尝试自己去加载。
这种模型保证了 Java 核心类库的安全性和一致性,避免了用户自定义的类覆盖 Java 核心类。