从“类加载的五个阶段”逐步分析 JVM“ 在每个阶段的内存变化”

下面从 类加载的五个阶段 逐步分析 JVM 在每个阶段的内存变化

(以 HotSpot + Java17 为例,Java8 以后方法区已改为 Metaspace)


JVM 相关内存区域速览

区域 作用 是否线程共享
堆(Heap) 对象实例、数组 共享
方法区 / 元空间 (Metaspace) 类的元信息、静态变量、常量池、JIT 代码 共享
运行时常量池 字面量、符号引用 共享
栈 (Java Stack) 局部变量、操作数栈、方法帧 线程私有
程序计数器 当前字节码行号 线程私有

类加载生命周期与内存变化

1️⃣ 加载 (Loading)

做的事

  • 通过 ClassLoader 读取 .class 字节码,形成 Class 对象。

内存变化

  • Metaspace:分配空间存放类的结构信息(字段、方法表、字节码指令)。
  • 运行时常量池:复制 class 文件中的常量池(符号引用、字符串字面量等)。
  • 堆/栈:暂时无变化。

2️⃣ 验证 (Verification)

做的事

  • 校验字节码格式、安全性(魔数、常量池索引、数据流、控制流)。

内存变化

  • Metaspace 中的类结构已存在,只进行一致性检查,没有额外分配大量内存。

3️⃣ 准备 (Preparation)

做的事

  • 为类的静态变量分配内存并设默认值(int=0、reference=null 等)。

内存变化

  • Metaspace

    • 分配静态变量所需空间,存储在类元数据区域。
    • 如果是 static final 且编译期常量,值直接存入运行时常量池。
  • 堆:此时不会创建对象实例。


4️⃣ 解析 (Resolution)

做的事

  • 把运行时常量池中的符号引用解析成直接引用(内存地址或句柄)。

内存变化

  • 运行时常量池中保存的直接引用指针被填充。
  • 可能触发其他类的加载/初始化,间接扩展 Metaspace 占用。

5️⃣ 初始化 (Initialization)

做的事

  • 执行 <clinit>:静态变量显式赋值、静态代码块。

内存变化

  • Metaspace:静态变量值被正式写入。

    • 如果静态变量是对象或集合,此时会在堆上创建对象实例并把引用放到 Metaspace 静态变量表。
  • :执行 <clinit> 方法帧入栈,执行完后出栈。


运行时继续变化

  • 实例化对象 → 在堆上分配实例内存。
  • 类卸载(ClassLoader 不再被引用)→ 其在 Metaspace 中的元数据、静态变量会被 GC 回收。

简化时序图

复制代码
Class 文件
   │
   ▼
[加载] ──► Metaspace: 类结构、常量池
   │
[验证] ──► 仅检查,无大变化
   │
[准备] ──► Metaspace: 静态变量默认值
   │
[解析] ──► 常量池符号引用 → 直接引用
   │
[初始化] ──► Metaspace: 赋真实值
   │          Heap: 静态对象实例

小结

  • 类元信息 + 静态变量存放在 Metaspace
  • 实例对象在 Heap
  • 执行方法时用栈
  • 类加载阶段的内存主要变化是 Metaspace 的增长 ,初始化阶段可能会触发 Heap 分配
相关推荐
寻星探路5 小时前
Java EE初阶启程记04---线程的状态
java·开发语言·jvm·java-ee
wshzrf15 小时前
【Java系列课程·Java学前须知】第3课 JDK,JVM,JRE的区别和优缺
java·开发语言·jvm
铅笔侠_小龙虾15 小时前
JVM 深入研究 -- 详解class 文件
java·开发语言·jvm
Just_Do_IT_OK15 小时前
线上JVM问题定位常用命令
jvm
DKPT1 天前
JVM栈溢出时如何dump栈信息?
java·jvm·笔记·学习·spring
DKPT1 天前
JVM堆大小如何设置?
java·开发语言·jvm·笔记·学习
铅笔侠_小龙虾1 天前
JVM 目录
java·jvm
花心蝴蝶.2 天前
JVM 类加载
开发语言·jvm·后端
老赵的博客2 天前
c++ 之多态虚函数表
java·jvm·c++