Java中String常量池(StringTable)从JDK 7起明确位于堆内存中,是JVM在堆上维护的独立哈希表,只存储字符串对象引用,由GC直接管理,与方法区、运行时常量池物理隔离且职责分明。

Java 中 String 字符串常量池(StringTable)既不在方法区,也不在“运行时常量池”里——它从 JDK 7 起就明确位于堆内存中,是 JVM 在堆上维护的一张独立哈希表,只存字符串对象的引用,由 GC 直接管理。
方法区里根本没有 StringTable
方法区(JDK 8+ 对应元空间)只存类元数据:类名、字段名、方法签名、符号引用等。其中的“运行时常量池”确实属于方法区,但它只加载 .class 文件里的静态常量项(比如数字、类名符号),并不存储实际的字符串对象。JDK 7 之后,字符串字面量不再进入运行时常量池,而是交由堆中的 StringTable 统一管理。
StringTable 不是方法区的子集,也不是运行时常量池的一部分
两者物理隔离、职责分明:
- 运行时常量池:每个类一份,随类加载而创建,内容来自 class 文件,不参与对象生命周期管理;
- StringTable:全局唯一,存在于堆中,存储的是对堆中字符串对象的引用,GC 可随时回收无引用的字符串;
- 调用 intern() 后的对象若无其他强引用,会被 GC 清理——这只有堆内结构才可能做到。
JDK 版本演进的关键节点
位置变化不是渐进调整,而是架构级迁移:
立即学习“Java免费学习笔记(深入)”;
- JDK 6 及之前:StringTable 和类元数据一起挤在永久代(PermGen),空间小、GC 滞后,容易 OOM;
- JDK 7:StringTable 整体搬入堆内存,存储内容变为引用而非对象本身,GC 频率提升,调优统一到堆参数;
- JDK 8 及以后:永久代被元空间取代,方法区概念弱化,StringTable 仍稳居堆中,底层仍是 C++ 实现的 native 哈希表,但内存由堆分配器提供。
怎么验证它真在堆里?
实操证据比文档更可靠:
- 用 jmap -histo 查看堆对象统计,intern 后的字符串会出现在堆实例列表中;
- 开启 GC 日志或使用 JFR,能看到 StringTable 条目随 Minor GC/Full GC 被清理;
- -XX:StringTableSize 参数只影响哈希桶数量,不改变内存归属——它调整的是堆内分配的 native 结构大小。


















