类加载机制、生命周期、类加载器层次、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 动态能力的核心基础。

相关推荐
一条咸鱼_SaltyFish23 分钟前
远程鉴权中心设计:HTTP 与 gRPC 的技术决策与实践
开发语言·网络·网络协议·程序人生·http·开源软件·个人开发
我即将远走丶或许也能高飞35 分钟前
vuex 和 pinia 的学习使用
开发语言·前端·javascript
沐知全栈开发42 分钟前
SQL LEN() 函数详解
开发语言
剑锋所指,所向披靡!1 小时前
C++之类模版
java·jvm·c++
钟离墨笺1 小时前
Go语言--2go基础-->基本数据类型
开发语言·前端·后端·golang
Coder_Boy_1 小时前
基于SpringAI的在线考试系统-0到1全流程研发:DDD、TDD与CICD协同实践
java·人工智能·spring boot·架构·ddd·tdd
sheji34161 小时前
【开题答辩全过程】以 面向高校校园的物物交换系统设计与实现为例,包含答辩的问题和答案
java·eclipse
小郭团队1 小时前
1_7_五段式SVPWM (传统算法反正切+DPWM3)算法理论与 MATLAB 实现详解
开发语言·嵌入式硬件·算法·matlab·dsp开发
鱼跃鹰飞1 小时前
Leetcode347:前K个高频元素
数据结构·算法·leetcode·面试
卓怡学长2 小时前
m115乐购游戏商城系统
java·前端·数据库·spring boot·spring·游戏