静态变量自JDK7起移至堆中,随Class对象存储;JDK8取消永久代,静态变量仍存于堆,元空间仅存类元数据。

静态变量在方法区的物理存放位置,确实随着JDK版本演进发生了本质变化——这不是微调,而是内存模型层面的重构。核心结论:JDK 7 是分水岭,JDK 8 彻底移除永久代,静态变量从“方法区的一部分”变为“堆中普通对象字段”的逻辑存在。
JDK 6 及之前:静态变量真正在永久代(PermGen)里
此时方法区由永久代实现,类元信息(Class对象、常量池、字段/方法数据)和静态变量都挤在一块连续的非堆内存中。比如定义 public static List<String> cache = new ArrayList<>();,这个 cache 引用本身存于永久代,而它指向的 ArrayList 实例则在堆中。
- 永久代大小需手动配置(-XX:PermSize / -XX:MaxPermSize)
- 容易因加载大量类或静态集合导致
java.lang.OutOfMemoryError: PermGen space - GC 对永久代回收效率低,Full GC 才会尝试清理
JDK 7:静态变量被“踢出”永久代,搬进堆(Heap)
这是关键过渡。Oracle 将字符串常量池(StringTable)和静态变量(static fields)统一移到堆中,但类元信息(Klass、Method、ConstantPool 等)仍留在永久代。也就是说:static 引用变量本身现在是堆里的普通对象字段,不再占用永久代空间。
- 例如
public static final byte[] CONFIG = new byte[1024*1024];,该字节数组对象及其引用都位于堆 - 永久代压力明显下降,但未根除;OOM 错误变为
java.lang.OutOfMemoryError: Java heap space更常见 - 类卸载机制仍未完善,动态类加载场景仍有泄漏风险
JDK 8+:永久代消失,元空间(Metaspace)接管类元数据,静态变量彻底属于堆
永久代被移除,取而代之的是本地内存(Native Memory)中的元空间。它只存类的元数据(如 Klass 结构、方法字节码、符号引用),完全不存静态变量。所有 static 字段——无论是否 final、是否引用大对象——其变量槽(field slot)和所引用的对象,全部位于堆内存。
- 静态变量生命周期与类加载器强绑定,类卸载时其引用才可能被回收
- 元空间默认无上限(受限于本地内存),用
-XX:MaxMetaspaceSize控制,OOM 错误变为java.lang.OutOfMemoryError: Metaspace - 堆内静态集合(如
static Map<?, ?> CACHE)成为内存泄漏高发区,需重点监控
理解这个变迁,不是为了背版本号,而是为定位真实问题:看到 OOM,先看错误提示后缀;排查静态泄漏,直接 dump 堆分析引用链;调优时,JDK 8 要分开关注堆大小和元空间大小。本质没变的是语义——static 仍属类型级别、随类共享;变的是 JVM 为它分配物理内存的策略和约束边界。

















