静态变量在jdk6存于永久代,jdk7起移至堆中,jdk8+仍存于堆、元空间仅存类元数据;迁移旨在提升gc效率并解决permgen oom问题。

静态变量在方法区的物理存放位置,不是“小修小补”,而是随 JDK 版本演进发生了内存模型层面的实质性迁移——关键不在“方法区”这个逻辑概念是否还存在,而在它背后由谁实现、静态变量是否还和类元数据“挤在一起”。说清这场质变,只需抓住三个版本锚点:JDK 6、JDK 7、JDK 8+。
JDK 6 及之前:静态变量真正在永久代里
此时方法区 = 永久代(PermGen),是一块连续的非堆内存。静态变量(包括引用本身)和类元信息(如常量池、字段描述、方法字节码)混存在同一区域。
- 例如
public static List<string> cache = new ArrayList();</string>,cache 这个引用变量存于永久代,而它指向的 ArrayList 实例才在堆中 - 永久代大小需手动配置(
-XX:PermSize/-XX:MaxPermSize),容易因加载大量类或静态集合触发java.lang.OutOfMemoryError: PermGen space - GC 对永久代回收效率低,通常只在 Full GC 时尝试清理,静态变量长期驻留风险高
JDK 7:静态变量被移出永久代,进入堆
这是真正意义上的分水岭。Oracle 主动将字符串常量池和所有静态变量(static fields)整体迁入 Java 堆,但类元数据仍留在永久代。
- 静态变量不再占用永久代空间,其引用和所指向的对象(如
static byte[] CONFIG = new byte[1MB];)全部位于堆中 - 永久代压力显著下降,OOM 错误类型转向更常见的
java.lang.OutOfMemoryError: Java heap space - 类卸载机制未同步完善,动态类加载场景下,若 ClassLoader 泄漏,静态变量仍无法回收
JDK 8 及以后:永久代消失,元空间接管元数据,静态变量彻底归属堆
永久代被完全移除,取而代之的是基于本地内存(Native Memory)的元空间(Metaspace)。它只存类结构、符号引用、方法字节码等元数据,不存任何静态变量。
- 所有 static 字段——无论是否
final、是否引用大对象——其字段槽(field slot)和实际对象都严格位于堆内存 - 静态变量生命周期与类加载器强绑定;只有类被卸载时,其静态引用才可能被 GC 回收
- 元空间默认无上限(受限于系统本地内存),OOM 错误变为
java.lang.OutOfMemoryError: Metaspace,需用-XX:MaxMetaspaceSize控制
所以,“方法区中静态变量在哪”这个问题,答案取决于你运行的 JDK 版本:不是方法区变了,而是方法区的物理实现换了,而静态变量被有意识地剥离出去了。定位 OOM,先看错误后缀;排查泄漏,直接 dump 堆分析静态引用链;调优时,JDK 8+ 必须分开关注堆大小和元空间大小。











