静态变量自jdk 6起经历三次迁移:jdk 6及之前存于永久代;jdk 7起移至堆内存,随class对象存储;jdk 8后稳居堆中,元空间仅存类元数据,不再包含静态变量。

静态变量的存储位置不是固定不变的,它随着 JDK 版本升级发生了实质性迁移:从 Java 6 及之前存于永久代(PermGen),到 Java 7 开始移入堆内存,再到 Java 8 彻底与元空间解耦、稳居堆中。
Java 6 及之前:静态变量在永久代
此时方法区由永久代实现,Class 对象和所有 static 字段(包括基本类型值和对象引用)都放在永久代里。比如 static int count = 1 的值、static String msg = "ok" 的引用地址,连同类元数据一起驻留在 PermGen。
- 永久代大小需手动配置(-XX:PermSize / -XX:MaxPermSize)
- 类卸载困难,动态生成大量类时易触发 OutOfMemoryError: PermGen space
- GC 对永久代支持弱,静态变量生命周期难以统一管理
Java 7:静态变量迁入堆,永久代开始瘦身
JDK 7 是关键转折点。HotSpot 将字符串常量池和静态变量整体搬出永久代,放入 Java 堆。Class 对象也首次进入堆内存,静态字段作为其隐式成员一并存放。
- 静态变量(无论 int、String 还是 List 引用)的“变量槽”本身已在堆中
- 永久代仍存在,但只保留类结构信息(如字节码、符号表),不再承载运行期静态数据
- 堆内 GC 可覆盖 Class 对象及其静态字段,为类卸载打下基础
Java 8 及以后:元空间接管元数据,静态变量扎根堆中
JDK 8 彻底移除永久代,引入基于本地内存的元空间(Metaspace)。注意:元空间只存类的描述性信息(运行时常量池、字段/方法签名、注解等),不存任何静态变量。
- java.lang.Class 实例分配在堆,其内存布局尾部直接附着所有 static 字段
- static 引用类型(如 static Map
cache = new HashMap() )的引用地址在堆中,new 出的对象实例也在堆(可能在年轻代或老年代) - 当 ClassLoader 被回收、Class 对象被卸载时,其所持有的静态变量自然随堆内存一同释放
为什么说“静态变量在方法区”已成历史说法?
“方法区”是 JVM 规范中的逻辑概念,不是物理内存区域。过去说静态变量在方法区,实质是因永久代实现了方法区功能;而今元空间虽承担方法区职责,却明确排除静态变量。真正承载静态变量的是堆——它既是 Class 对象的落脚地,也是所有 static 字段的归属处。
这种变化让类加载、卸载、GC 更统一可控,也消除了 PermGen 的调优负担和 OOM 风险。











