static数据统一存于方法区(JDK 8+为元空间),属类级别资源,随类加载初始化、类卸载释放;不存堆或栈,因需全局共享且长期存在,堆会导致冗余复制,栈则无法持久。

Java 中 static 修饰的数据(变量、方法、代码块)统一存放在方法区(JDK 8+ 称为元空间,MetaSpace),而不是堆或栈。它属于类级别资源,随类加载而初始化,生命周期与类一致——类加载时分配内存,JVM 退出时才释放。
static 变量存在哪儿?
静态变量(类变量)不随对象创建而复制,只在类加载阶段在方法区分配一次内存。无论你 new 多少个对象,所有实例共享同一份 static 变量,修改任一地方,其他地方立即可见。
- JDK 7 及以前:存于方法区的“永久代”(PermGen)
- JDK 8 开始:永久代被移除,static 数据改存于元空间(MetaSpace),底层基于本地内存(Native Memory)
- 注意:元空间仍属于方法区的逻辑范畴,不是堆内存——所以不能说 static 存在“堆里”
为什么不能存在堆或栈?
堆用于存放对象实例和实例变量(每个 new 都一份),栈用于存放局部变量和方法调用帧(方法结束即销毁)。而 static 的特点是独立于对象、长期存在、全局共享,这两块区域都不满足要求:
- 如果放堆里 → 每次 new 对象都复制一份,违背“共享”初衷(比如 100 个对象存 100 个相同的配置字符串,严重浪费)
- 如果放栈里 → 方法执行完就消失,无法支撑类加载后一直可用的语义
static 方法和静态代码块呢?
它们也存于方法区(元空间),但内容不同:
立即学习“Java免费学习笔记(深入)”;
- 静态方法:字节码指令 + 符号引用信息,不占用额外数据空间,调用时直接从方法区读取执行
- 静态代码块:在类加载的“初始化阶段”执行一次,其内部赋值操作会写入对应的 static 变量(仍在方法区)
- 二者都没有 this 引用,也不能访问非 static 成员——因为它们不属于任何对象,只属于类本身
一个直观对比:static vs 实例变量
假设定义:public class Counter { public static int count = 0; public int id = 1; }
-
Counter.count→ 存在元空间,只有一个整数空间,所有 Counter 实例共用 -
new Counter().id→ 每次 new 都在堆中新开辟一块内存,存一个独立的 int,互不影响 - 即使没有创建任何 Counter 对象,
Counter.count依然存在且可访问


















