类加载机制、生命周期、类加载器层次、JVM的类加载方式

我理解 Java 的类加载机制,其实是 JVM 在运行时动态地将 .class 字节码文件加载到内存,

并完成从"字节码"到"可执行 Java 类"的完整生命周期。

这个过程体现了 Java 语言动态性的底层基础,也是热替换、反射、SPI 机制的关键。

一、类加载的生命周期(Class Life Cycle)

JVM 从一个类被首次主动使用,到成为可执行状态,会经历 五个核心阶段:

mathematica 复制代码
加载(Loading)
→ 验证(Verification)
→ 准备(Preparation)
→ 解析(Resolution)
→ 初始化(Initialization)
(使用(Using))
(卸载(Unloading))

下面按顺序讲清楚每个步骤的作用 👇


1️⃣ 加载(Loading)

JVM 做三件事:

  1. 通过类加载器查找字节码文件
    可能来自:
    • 磁盘 .class
    • JAR 包
    • 网络(RMI)
    • 动态生成(如 CGLIB、ASM)
  2. 将字节码读取到内存
  3. 创建 Class 对象
    在方法区(JDK8之后是元空间)中为该类创建一个 Class 对象。

Class 对象是反射的基础。


2️⃣ 验证(Verification)

确保字节码是合法的、没有被篡改。

包括:

  • 文件格式验证
  • 元数据验证(父类是否存在、是否继承正确)
  • 字节码验证(指令合法)
  • 符号引用验证

这是安全性最强的一步,用来防止非法字节码攻击。


3️⃣ 准备(Preparation)

给**类变量(static变量)**分配内存,并赋默认值。

⚠️ 注意:

  • 不是 static 代码块!
  • 不是 static 的显示初始化!

示例:

java 复制代码
public static int a = 10;

准备阶段只会执行:

java 复制代码
a = 0;

4️⃣ 解析(Resolution)

将常量池中的各种符号引用(Symbolic Reference)替换为直接引用(Direct Reference)。

比如:

  • 类名 → 指向具体类的引用
  • 字段名 → 指向字段偏移量
  • 方法名 → 指向具体方法表

该阶段可能在运行时才进行,是动态链接的重要部分。


5️⃣ 初始化(Initialization)

真正执行 static 代码和 static 变量显式赋值的阶段。

示例:

java 复制代码
public static int a = 10;
static { a = 20; }

初始化阶段的执行顺序就是 JVM 构建 <clinit> 方法的过程。


二、类加载器的层次结构(ClassLoader Hierarchy)

Java 类加载器采用双亲委派模型(Parent Delegation Model),层次如下:

pgsql 复制代码
Bootstrap ClassLoader(C++实现,没有Class对象)
↓
Extension ClassLoader(Java)
↓
System/App ClassLoader(Java)
↓
User-defined ClassLoader(用户自定义)

1️⃣ Bootstrap ClassLoader

  • 用 C++ 实现
  • 加载 rt.jar 中的核心类(如 java.lang.*)

2️⃣ ExtClassLoader(扩展类加载器)

  • 加载 JAVA_HOME/lib/ext/*.jar

3️⃣ AppClassLoader(应用类加载器)

  • 加载 classpath 下的类

4️⃣ Custom ClassLoader(自定义)

常用于:

  • Tomcat / Spring 的模块隔离
  • SPI
  • 加密类加载

三、双亲委派机制(Parent Delegation)

核心思想:

自底向上请求,自顶向下加载。

流程:

  1. 子加载器收到类加载请求;
  2. 委托给父加载器;
  3. 父加载器找不到才由子加载器加载。

好处:

  • 防止重复加载
  • 保证核心 API 的安全性(你不能伪造 java.lang.String)

四、JVM 的三种主要类加载机制

你可以这样总结:

Java 的类加载机制是"按需加载 + 动态链接 + 可替换"。

主要有三种:


1️⃣ 隐式加载(最常见)

由 JVM 自动触发,例如:

  • new 对象
  • 调用静态方法
  • 调用静态字段
  • 反射使用类
  • 子类初始化前必定初始化父类

2️⃣ 显式加载

代码中主动调用:

java 复制代码
Class.forName("com.demo.User");
ClassLoader.loadClass("com.demo.User");

区别:

方法 是否会初始化类
Class.forName 会执行初始化(执行 static 代码块)
ClassLoader.loadClass 只加载,不初始化

3️⃣ 自定义类加载器

重写 findClass()

常用于:

  • Tomcat WebApp 模块隔离
  • 热部署(OSGi)
  • 加密 class 加载
  • 插件化框架

五、类加载器破坏双亲委派的场景

可额外加分:

  • JDBC SPI(使用了线程上下文类加载器)
  • Tomcat 自定义隔离类加载机制
  • OSGi 多版本模块加载
  • JNDI、ServiceLoader

六、个人理解总结(重点提升面试亮点)

我觉得类加载机制背后体现的是 Java "运行期动态行为"的能力。

双亲委派保证安全性,生命周期保证加载正确性,自定义类加载器保证扩展性。

这套机制是 Java 容器(Tomcat、Spring Boot 热更新、插件框架等)的底层基础。


✅ 一句话总结:

Java 的类加载机制由"加载 → 验证 → 准备 → 解析 → 初始化"构成,

通过双亲委派模型保证安全性,

通过三种加载方式保证灵活性,

是整个 JVM 动态能力的核心基础。

相关推荐
修行者Java2 小时前
JVM 垃圾回收算法的详细介绍
jvm·算法
程序员爱钓鱼2 小时前
Python 编程实战 · 实用工具与库 — Flask 基础入门
后端·python·面试
一 乐2 小时前
海产品销售系统|海鲜商城购物|基于SprinBoot+vue的海鲜商城系统(源码+数据库+文档)
java·前端·javascript·数据库·vue.js·后端
程序员爱钓鱼2 小时前
Python编程实战 - Python实用工具与库 - 文件批量处理脚本
后端·python·面试
mjhcsp3 小时前
C++ 三分查找:在单调与凸函数中高效定位极值的算法
开发语言·c++·算法
我命由我123453 小时前
Element Plus 组件库 - Select 选择器 value 为 index 时的一些问题
开发语言·前端·javascript·vue.js·html·ecmascript·js
沐知全栈开发3 小时前
MySQL 删除数据库指南
开发语言
qq. 28040339844 小时前
js 原型链分析
开发语言·javascript·ecmascript
大飞哥~BigFei4 小时前
RabbitMq消费延迟衰减重试实现思路
java·分布式·rabbitmq