静态变量存储位置取决于JDK版本:JDK 7及以前在永久代(方法区),JDK 8+移至堆中,元空间仅存类元数据;其值(对象或基本类型)实际在堆分配,由Class对象引用持有。

Java 中的 static 静态变量并不直接“加载”在方法区中,而是随着类的初始化,在类加载过程的 初始化阶段 被分配内存并赋值,其存储位置取决于变量类型:
静态变量的存储位置不是固定的“方法区”
从 JDK 7 开始,运行时常量池和静态变量不再存放在永久代(PermGen);JDK 8 及以后彻底移除了永久代,改用元空间(Metaspace)存放类的元数据(如类名、字段/方法信息、常量池等),而静态变量本身被分配在堆(Heap)中。
也就是说: - 类的结构信息(如字段描述、方法字节码、符号引用等)存在元空间(原方法区逻辑位置); - static 变量的值(对象实例或基本类型数据)实际存放在 Java 堆里,只是由类对象(Class 实例)持有引用。
静态变量何时被加载和初始化?
它发生在类加载过程的 初始化阶段(Initialization),前提是该类首次被主动使用(如 new 实例、调用静态方法、访问静态字段等)。这个阶段会执行 <clinit> 方法(编译器自动生成的静态初始化块和静态变量赋值语句的合并)。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 如果静态变量是基本类型或字符串字面量,可能在准备阶段就完成默认值或常量赋值;
- 如果是对象引用(如
static List<String> list = new ArrayList<>();),则在初始化阶段执行 new 操作,对象分配在堆中,引用存于类对象的静态字段里; - 类加载器将类的二进制流解析后,先在元空间中生成 Class 对象,再为 static 字段在堆中分配空间并初始化。
怎么验证静态变量在堆中?
可以通过 JVM 工具观察:
立即学习“Java免费学习笔记(深入)”;
- 用
jmap -histo <pid>查看堆中对象分布,能发现你定义的静态集合或缓存对象; - 用 JOL(Java Object Layout)工具打印类对象布局,会看到 static 字段不占 Class 实例自身内存,但其引用指向堆中真实对象;
- 触发 Full GC 后,若静态集合持有大量对象,这些对象不会被回收——因为被 Class 对象的静态引用强持有。
常见误区提醒
很多人误以为“static 变量在方法区”,其实是混淆了变量引用的归属位置和变量值的实际存储位置:
- 方法区(元空间)存的是“类的模板信息”,不是变量值;
- 每个类只有一个 Class 对象(由类加载器创建),它作为普通 Java 对象存在于堆中,其内部包含对所有 static 字段的引用;
- 所以 static 变量的生命周期与类相同,但内存实体在堆,受 GC 管理(除非被 Class 对象强引用)。

















