字符串常量池位置变化:JDK6及以前在永久代存对象本身,JDK7起移至堆中仅存引用,JDK8+仍位于堆内与元空间分离;此举解决PermGen OOM、提升GC效率并适配方法区重构。

字符串常量池的位置变化是 Java 面试中高频考点,关键不在于死记版本号,而在于理解“为什么调”和“调了之后行为怎么变”。它本质是 JVM 内存管理演进的缩影,直接关联 intern() 行为、GC 响应和 OOM 类型。
JDK 6 及以前:常量池在永久代,存的是对象本身
此时字符串常量池属于方法区,HotSpot 用永久代(PermGen)实现。所有双引号字面量(如 "abc")和 intern() 的字符串,都会在永久代中创建完整对象实例。
- 永久代空间小(默认几十 MB),且只在 Full GC 时回收,容易触发
java.lang.OutOfMemoryError: PermGen space -
intern()会把堆中字符串拷贝一份到永久代,造成冗余副本 - StringTable 默认大小仅 1009,哈希冲突多时性能下降明显
JDK 7:迁入堆内存,只存引用,不复制对象
这是最关键的转变。字符串常量池整体从永久代搬到了 Java 堆中,存储方式也从“存对象”变为“存引用”。
Java开发手册规约集合,基于阿里巴巴Java开发手册(嵩山版)。 涵盖7大维度:编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程结构、设计规约。 当用户需要:(1) 编写或审查Java代码 (2) 检查命名/代码规范 (3) 处理异常和日志 (4) 编写单元测试 (5) 安全编码 (6) 数据库设...
- 字符串对象本体(无论字面量还是 new 出的)统一在堆中分配;常量池只是用哈希表(
StringTable)保存这些对象的引用 - 堆支持 Minor GC 和 Full GC,无用字符串可被及时回收,OOM 风险大幅降低
-
intern()不再新建对象,而是登记已有堆对象的引用;若已存在相同内容字符串,则直接复用 - StringTable 默认容量扩大到 60013,可通过
-XX:StringTableSize调整
JDK 8 及以后:仍在堆中,与元空间彻底分离
JDK 8 废除永久代,引入元空间(Metaspace)存放类元数据、运行时常量池等,但字符串常量池并未进入元空间,仍稳居堆内存中。
立即学习“Java免费学习笔记(深入)”;
- 运行时常量池(来自 .class 文件的符号引用部分)进了元空间;字符串常量池仍由堆中的
StringTable管理 - 调用大量
intern()触发的 OOM 错误是java.lang.OutOfMemoryError: Java heap space,而非 Metaspace 错误——这是最直接的实验证据 - 常量池大小不再受
-XX:MaxPermSize限制,但受限于堆总大小(-Xmx) - 元空间只管类结构信息,不适用于字符串高频创建销毁的场景,所以没把它挪过去
面试回答建议:紧扣行为差异
不要只说“JDK6在永久代,JDK7在堆”,要带出影响:
- 问到
intern(),强调 JDK6 是复制对象,JDK7+ 是登记引用 - 问到 OOM,指出错误类型不同:JDK6 是
PermGen space,JDK8+ 是Java heap space - 问到 GC,说明 JDK7+ 后常量池字符串可被正常回收,而 JDK6 下基本“永驻”
- 提到配置参数时,对比
-XX:MaxPermSize(JDK6)、-XX:StringTableSize(全版本可用)、-Xmx(JDK7+ 主要约束)

















