字符串常量池位置变化核心是解决permgen oom、提升gc效率及适配方法区重构:jdk6前在永久代存对象;jdk7起移至堆中仅存引用;jdk8+仍驻堆内,与元空间分离。

字符串常量池的位置变化,核心是为了解决永久代内存紧张、GC效率低和应用规模扩大带来的实际问题,不是简单迁移,而是 JVM 内存模型演进的关键一环。
JDK 6 及以前:在永久代(PermGen),存的是完整字符串对象
此时字符串常量池属于方法区,而 HotSpot 用永久代实现方法区。所有双引号字面量(如 "abc")和调用 intern() 的字符串,都会在永久代中创建一份独立的对象实例。
- 永久代空间小(默认仅几 MB),且只在 Full GC 时回收,极易触发 OutOfMemoryError: PermGen space
- intern() 会把堆中字符串拷贝一份到永久代,产生冗余副本,增加内存压力
- StringTable 默认大小仅 1009,哈希冲突多时链表过长,intern() 查找性能明显下降
JDK 7:迁入堆内存,只存引用,不再复制对象
这是最关键的转变。字符串常量池整体从永久代移到 Java 堆中,存储方式由“存对象”变为“存引用”。
- 字符串本体(无论字面量还是 new 出的)统一在堆中分配;常量池只是用 StringTable 保存这些对象的引用
- 堆支持 Minor GC 和 Full GC,无用字符串可被及时回收,OOM 风险大幅降低
- intern() 不再新建对象,而是登记已有堆对象的引用;若已存在相同内容字符串,直接复用
- StringTable 默认容量扩大到 60013,可通过 -XX:StringTableSize 调整
JDK 8 及以后:仍在堆中,与元空间彻底分离
JDK 8 废除永久代,引入元空间(Metaspace)存放类元数据、运行时常量池等,但字符串常量池并未进入元空间,仍稳定位于堆内存中。
- 运行时常量池(来自 .class 文件的符号引用部分)进了元空间;字符串常量池仍由堆中的 StringTable 管理
- 大量 intern() 触发的 OOM 错误是 java.lang.OutOfMemoryError: Java heap space,而非 Metaspace 错误——这是最直接的实验证据
- 常量池大小不再受 -XX:MaxPermSize 限制,但受限于堆总大小(-Xmx)
- 元空间专用于类结构信息,不适合字符串高频创建销毁的场景,所以没把它挪过去
本质上,字符串常量池始终依附于方法区的物理实现而变迁,它的位置变化直接改变了 intern() 行为、GC 响应和内存配置策略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











