类加载子系统的作用是什么?如何工作的?

目录

一、类加载子系统的核心作用

二、类加载子系统的工作流程

[阶段 1:加载(Loading)------"读取字节码,生成 Class 对象"](#阶段 1:加载(Loading)——“读取字节码,生成 Class 对象”)

[阶段 2:链接(Linking)------"验证 + 准备 + 解析,保证类的合法性"](#阶段 2:链接(Linking)——“验证 + 准备 + 解析,保证类的合法性”)

[子阶段 2.1:验证(Verification)------"校验字节码的合法性"](#子阶段 2.1:验证(Verification)——“校验字节码的合法性”)

[子阶段 2.2:准备(Preparation)------"为静态变量分配内存并赋默认值"](#子阶段 2.2:准备(Preparation)——“为静态变量分配内存并赋默认值”)

[子阶段 2.3:解析(Resolution)------"将符号引用转换为直接引用"](#子阶段 2.3:解析(Resolution)——“将符号引用转换为直接引用”)

[阶段 3:初始化(Initialization)------"执行静态代码,赋静态变量最终值"](#阶段 3:初始化(Initialization)——“执行静态代码,赋静态变量最终值”)

三、类加载子系统的核心支撑:类加载器与双亲委派模型

[1. 类加载器的分类(JDK8 为例)](#1. 类加载器的分类(JDK8 为例))

[2. 双亲委派模型的工作逻辑](#2. 双亲委派模型的工作逻辑)

[四、类加载子系统与 JVM 运行时数据区的关联](#四、类加载子系统与 JVM 运行时数据区的关联)

总结

核心作用

核心工作流程

关键特征

类加载子系统的核心作用是将编译后的.class 字节码文件加载到 JVM 内存中,并转换为方法区的类元数据,同时创建堆中的 Class 对象,为后续字节码执行提供基础;其工作遵循 "加载 - 链接 - 初始化" 的完整流程,且通过类加载器的双亲委派模型保证类加载的安全性和唯一性。

一、类加载子系统的核心作用

类加载子系统是 JVM 启动后第一个处理 Java 类的核心模块,所有 Java 类(包括自定义类、JDK 核心类)都必须经过它的处理才能被 JVM 执行,核心作用可总结为 3 点:

  1. 加载字节码文件 :从磁盘、网络、内存等数据源读取.class文件(字节码),将其转换为 JVM 可识别的二进制流;
  2. 构建类元数据 :将字节码中的类信息(类名、父类、接口、字段、方法、常量等)解析后存储到方法区,形成该类的元数据;
  3. 创建 Class 对象 :在 中创建对应类的java.lang.Class对象(作为方法区类元数据的访问入口),后续通过该对象实现反射、实例化等操作。

补充:类加载子系统仅负责 "加载类",不负责 "执行类"(执行由 JVM 执行引擎完成),且加载后的类元数据会永久存储在方法区(JDK1.8 + 为元空间),直到类卸载时才释放。

二、类加载子系统的工作流程

类加载的完整流程分为 3 个阶段,其中 "链接" 又细分为 3 个子阶段,整体流程严格按顺序执行(个别子阶段可交叉执行,但核心顺序不变):

阶段 1:加载(Loading)------"读取字节码,生成 Class 对象"

这是类加载的第一步,核心是 "找到并加载类的字节码",具体做 3 件事:

  1. 获取字节码流 :类加载器根据类的全限定名(如java.lang.String),从指定数据源(磁盘classpath、JAR 包、网络、动态生成等)读取.class文件的二进制字节流;
  2. 转换存储格式 :将字节流转换为 JVM 方法区的类元数据结构(存储类的版本、字段、方法、常量池等信息);
  3. 创建 Class 对象 :在堆中生成一个代表该类的java.lang.Class对象,作为访问方法区类元数据的 "唯一入口"(后续通过Class.forName()、对象getClass()获取的都是这个对象)。

关键:加载阶段的核心是类加载器(如 Bootstrap、Extension、Application、自定义加载器)的工作,不同类加载器负责加载不同来源的类(如 Bootstrap 加载 JRE/lib 核心类)。

阶段 2:链接(Linking)------"验证 + 准备 + 解析,保证类的合法性"

链接阶段是对加载后的类进行 "校验和预处理",确保类符合 JVM 规范,可安全执行,分为 3 个子阶段:

子阶段 2.1:验证(Verification)------"校验字节码的合法性"

这是 JVM 安全的重要保障,核心是检查字节码是否符合 JVM 规范,避免恶意或错误的字节码导致 JVM 崩溃,主要校验:

  • 文件格式验证:检查字节流是否符合 Class 文件格式规范(如魔数0xCAFEBABE、版本号是否兼容当前 JVM);
  • 元数据验证:检查类的元数据(如是否继承了final类、方法参数类型是否合法、是否有重复的字段 / 方法);
  • 字节码验证:检查方法的字节码指令是否合法(如指令操作数类型匹配、跳转地址有效);
  • 符号引用验证:检查类的符号引用(如引用的类 / 方法 / 字段是否存在)。

注:若验证失败,会抛出VerifyError异常(如自定义一个不符合规范的 Class 文件,加载时就会触发)。

子阶段 2.2:准备(Preparation)------"为静态变量分配内存并赋默认值"

核心是 "初始化类的静态变量(类变量)",具体规则:

  1. 为方法区中类的静态变量(static 修饰) 分配内存空间;
  2. 将静态变量初始化为默认值 (而非代码中定义的初始值):
    • 基本类型(int、boolean 等):默认值为 0、false 等;
    • 引用类型:默认值为null
    • 示例:代码中定义static int a = 10;,准备阶段会为a分配内存,赋值为 0(10 会在 "初始化" 阶段赋值)。

关键:仅处理静态变量 ,实例变量(非 static)不处理(实例变量在对象实例化时分配到堆中);static final常量除外(编译期已确定值,准备阶段直接赋最终值,如static final int b = 20;,准备阶段赋值 20)。

子阶段 2.3:解析(Resolution)------"将符号引用转换为直接引用"

核心是 "解析类的符号引用(字符串形式)为直接引用(内存地址)":

  • 符号引用:Class 文件中用字符串描述的类、方法、字段的引用(如java.lang.Objectadd(int));
  • 直接引用:指向方法区类元数据的内存地址或偏移量(可直接访问的引用);
  • 示例:类中调用Object obj = new Object();,解析阶段会将 "java.lang.Object" 这个符号引用,转换为方法区中Object类元数据的直接内存地址。

注:解析阶段可延迟到 "首次使用该引用时" 执行(如首次调用方法时才解析),而非必须在链接阶段完成,目的是提升类加载效率。

阶段 3:初始化(Initialization)------"执行静态代码,赋静态变量最终值"

这是类加载的最后一步,也是唯一会执行 Java 代码(字节码)的阶段,核心是 "执行类的初始化逻辑",触发条件是:

  • 首次创建类的实例(new XXX());
  • 首次调用类的静态方法 / 访问静态变量(除static final常量);
  • 通过反射访问类(Class.forName("com.example.Test"));
  • 初始化子类时,先初始化父类;
  • JVM 启动时,初始化包含main()方法的主类。

初始化阶段的具体操作:

  1. 执行类的静态代码块(static {})
  2. 为静态变量赋代码中定义的最终值 (如static int a = 10;,此时将 a 从 0 改为 10);
  3. 按 "静态变量声明顺序、静态代码块书写顺序" 执行(静态代码块可访问前面声明的静态变量,不可访问后面的)。

关键:初始化阶段仅执行一次(JVM 保证类的初始化是线程安全的),若初始化过程中抛出异常,该类会标记为 "初始化失败",后续无法再使用。

三、类加载子系统的核心支撑:类加载器与双亲委派模型

类加载子系统的 "加载" 阶段由类加载器完成,而类加载器遵循的双亲委派模型是保证类加载安全和唯一性的核心:

1. 类加载器的分类(JDK8 为例)

表格

类加载器类型 核心作用 加载来源
启动类加载器(Bootstrap) 加载 JVM 核心类 JRE/lib/rt.jar、核心类库(如 java.lang.*)
扩展类加载器(Extension) 加载 JVM 扩展类 JRE/lib/ext/*.jar
应用类加载器(Application) 加载应用程序类 classpath 下的类、JAR 包
自定义类加载器 加载自定义来源的类(如 Tomcat 的 WebappClassLoader) 自定义路径(如 WAR 包、网络)
2. 双亲委派模型的工作逻辑

类加载器加载类时,会先委托父加载器加载,父加载器加载不到时才自己加载,流程:

  1. 应用类加载器收到加载请求,先委托给扩展类加载器;
  2. 扩展类加载器再委托给启动类加载器;
  3. 启动类加载器尝试加载:若能加载则返回 Class 对象,若加载不到(如不是核心类),返回给扩展类加载器;
  4. 扩展类加载器尝试加载:若能加载则返回,否则返回给应用类加载器;
  5. 应用类加载器最后尝试加载:若仍加载不到,抛出ClassNotFoundException

核心价值:避免类重复加载(如java.lang.String只能由启动类加载器加载),防止恶意类篡改核心类(如自定义java.lang.String会被双亲委派模型拦截,无法加载)。

四、类加载子系统与 JVM 运行时数据区的关联

类加载子系统的工作全程依赖 JVM 运行时数据区,核心关联:

  1. 加载阶段:字节码转换的类元数据存储到方法区 ,Class 对象创建到
  2. 链接阶段:准备阶段为静态变量分配的内存位于方法区
  3. 初始化阶段:执行静态代码块的临时数据存储到Java 虚拟机栈(栈帧的操作数栈、局部变量表);
  4. 类加载完成后,执行引擎通过堆中的 Class 对象访问方法区的类元数据,调用方法时在 Java 虚拟机栈创建栈帧,实例化对象时在堆分配内存。

总结

核心作用
  1. 读取.class 字节码文件,将类元数据加载到方法区,为执行引擎提供可执行的类信息;
  2. 创建堆中的 Class 对象,作为访问类元数据的入口,支撑反射、实例化等操作;
  3. 通过验证、双亲委派模型保证类加载的合法性和安全性。
核心工作流程
  1. 加载:读取字节码,生成 Class 对象(方法区存元数据,堆存 Class 对象);
  2. 链接:验证(校验合法性)→ 准备(静态变量赋默认值)→ 解析(符号引用转直接引用);
  3. 初始化:执行静态代码块,为静态变量赋最终值(仅首次使用时执行)。
关键特征
  • 类加载是 "懒加载" 的(仅首次使用时触发),而非 JVM 启动时全部加载;
  • 初始化阶段是唯一执行 Java 代码的阶段,且线程安全;
  • 双亲委派模型保证核心类不被篡改,避免类重复加载。
相关推荐
CaracalTiger20 小时前
如何解决Unexpected token ‘<’, “<!doctype “… is not valid JSON 报错问题
java·开发语言·jvm·spring boot·python·spring cloud·json
江湖有缘1 天前
自托管RSS解决方案:Docker化Fusion安装教程
java·jvm·docker
Chan161 天前
《深入理解Java虚拟机》| 类加载与双亲委派机制
java·开发语言·jvm·面试·java-ee·intellij-idea
闻哥2 天前
GET和POST请求的本质区别
java·网络·jvm·spring·http·面试·https
野犬寒鸦2 天前
从零起步学习并发编程 || 第八章:ThreadLocal深层解析及常见问题:避坑指南与最佳实践
java·服务器·开发语言·jvm·算法
生命因何探索2 天前
JVM知识汇总
jvm
heartbeat..3 天前
Java 中的类加载器的双亲委派模型:原理、层级与实现
java·开发语言·jvm·类加载器
Mr Aokey3 天前
JVM三剑客:内存模型、类加载机制与垃圾回收精讲
java·jvm
程序员ken3 天前
献给自己的一款个人管理的桌面软件(一)
java·开发语言·jvm
团子的二进制世界3 天前
JVM 运行时数据区的 7 大组成部分
jvm