答案取决于JDK版本:JDK 6及以前静态变量存于永久代(方法区),JDK 7起移至堆中,JDK 8+仍存于堆、元空间仅存类元数据;迁移本质是物理实现变更,而非逻辑概念调整。

静态变量到底存在堆里,还是方法区?答案取决于JVM版本——不是非此即彼,而是随JDK演进而变化。
JDK 7及以前:静态变量在永久代(方法区)
那时方法区的物理实现是永久代(PermGen),静态变量和类元数据(如常量池、字段描述符)一起存放在其中。类加载时就分配内存,不依赖对象创建,生命周期与类一致。比如:
-
public static List<String> cache = new ArrayList<>(); —— 这个
cache引用本身和它指向的ArrayList实例,都放在永久代中 - OOM错误提示通常是
java.lang.OutOfMemoryError: PermGen space
JDK 8开始:静态变量实际在堆中
永久代被移除,元空间(Metaspace)取而代之,只存类结构信息(Klass、符号表、常量池等),不再存放任何静态变量。所有static字段连同Class对象一起,移到堆内存中:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- Class对象首次完整进入堆,静态变量作为其隐式字段存在
-
public static final byte[] DATA = new byte[1024 * 1024];—— 数组对象和字段引用均在堆中分配 - GC可统一管理这些静态引用,但只有ClassLoader被回收、类卸载后,它们才可能被清理
为什么容易混淆?关键看“方法区”指什么
JVM规范说静态变量在“方法区”,这是逻辑概念;而具体实现会变:
立即学习“Java免费学习笔记(深入)”;
- 规范层面:静态变量属于类元数据的一部分,归入方法区语义范畴
- 物理层面:JDK 7起已迁入堆;JDK 8+明确由堆承载,元空间纯做类描述
- 面试或考试若未指定JDK版本,答“方法区”属传统表述;但生产环境排查内存问题,必须按实际版本判断位置
对开发的实际影响
存储位置变化直接关系到问题定位和调优策略:
- 堆溢出(
java.lang.OutOfMemoryError: Java heap space)要检查静态集合是否持续add未清理 - 元空间溢出(
java.lang.OutOfMemoryError: Metaspace)和静态变量无关,应查动态生成类(如CGLIB、反射大量加载) - JDK 8+需分开配置:
-Xmx控堆大小,-XX:MaxMetaspaceSize控元空间上限

















