运行时常量池始终属于方法区,jdk 6在永久代,jdk 7仍在永久代但字符串字面量被剥离,jdk 8起随方法区迁移至元空间;字符串常量池jdk 6在永久代,jdk 7起移至堆中,实现解耦、支持gc回收并适配大规模应用。

运行时常量池和字符串常量池的位置变化,本质是JVM内存管理演进的缩影,不是随意调整,而是为解决实际问题:永久代空间小、OOM频发、类加载与字符串复用逻辑耦合过紧。
运行时常量池在哪?看方法区怎么实现
运行时常量池始终属于方法区(Method Area)的运行时表现,但方法区本身的物理实现随JDK版本迁移:
- JDK 6及以前:方法区由永久代(PermGen)实现,运行时常量池就在PermGen里
- JDK 7:方法区仍用PermGen,但运行时常量池内容开始“瘦身”——符号引用部分保留在PermGen,而字符串字面量等被剥离出去
- JDK 8起:永久代被彻底移除,方法区改由元空间(Metaspace)实现(使用本地内存),运行时常量池也随之进入元空间
字符串常量池在哪?关键在“复用”需求驱动迁移
字符串常量池专用于字符串字面量(如"abc")和intern()结果的存储与复用,它的位置变化更激进,直接反映对堆内存统一管理的转向:
- JDK 6:和运行时常量池一起挤在PermGen中,大小受限(默认仅4MB),
intern()滥用极易触发OutOfMemoryError: PermGen space - JDK 7:迁移到Java堆(Heap)中,与普通对象共用年轻代/老年代。这是重大分水岭——从此字符串复用不再受PermGen容量制约,GC也能回收无用字符串
- JDK 8及以后:继续保留在堆中,与元空间解耦。此时运行时常量池(在元空间)和字符串常量池(在堆)彻底分离,各司其职
为什么JDK 7要把字符串常量池搬到堆里?
核心就三点:
- 解耦内存压力:PermGen原本要存类元数据、静态变量、运行时常量池,再塞大量字符串,容易撑爆;堆空间更灵活可调
-
支持GC回收:PermGen基本不参与GC,字符串一旦进入就很难释放;堆中的字符串能被常规GC清理,尤其适合生命周期短的
intern()字符串 - 适配应用规模:现代应用动辄加载成千上万个类、处理海量字符串,堆比PermGen更适合承载这类动态增长的数据
两个池现在的关系:同源不同域
它们都源于class文件里的常量池,但运行期已完全独立:
- 编译期的
"hello"先写入class常量池 → 类加载时,该字面量可能进入运行时常量池(作为符号引用),也一定触发字符串常量池检查(是否已存在,否则加入) - 运行时常量池管的是类结构信息(类名、方法签名、字段描述等),字符串常量池只管字符串实例的唯一性
- 两者地址空间不同:元空间 vs 堆 → 所以
==比较必然为false,哪怕内容相同











