stringtable自jdk 6起经历三次关键迁移:jdk 6及之前位于永久代,jdk 7起迁入堆内存并改为存储引用,jdk 9后仍驻堆中但因compact strings优化内存占用与gc效率。

StringTable(字符串常量池)在 JVM 中的位置不是一成不变的,而是随着 JDK 版本演进发生了关键迁移:从永久代 → 堆内存 → 保持在堆内但结构持续优化。
Java 6 及之前:位于永久代(PermGen)
字符串字面量(如 "hello")直接存入方法区的运行时常量池,而 StringTable 作为其一部分,也驻留在永久代中。
- 永久代空间有限且 GC 机制滞后——只有 Full GC 才会清理,回收时机晚、效率低
- 大量字符串 intern 容易触发 java.lang.OutOfMemoryError: PermGen space
- 默认大小仅 1009,哈希冲突严重时链表过长,intern() 性能急剧下降
JDK 7–8:迁入 Java 堆,只存引用不存对象
这是最实质性的变更。StringTable 整体从永久代搬至堆内存,存储内容由“字符串对象本身”变为“指向堆中字符串对象的引用”。
- 堆内存可被 Minor GC 和 Full GC 频繁回收,常量池中无用字符串能及时释放
- 不再受永久代大小限制,调优只需关注堆参数(如 -Xmx),更统一简洁
- 默认容量提升至 60013(素数),降低哈希碰撞概率;可通过 -XX:StringTableSize 手动调整
JDK 9 及以后:仍在堆中,但底层存储与 GC 协同更紧密
位置未再变动,但 String 对象自身结构升级(Compact Strings),间接影响 StringTable 的内存占用与访问效率。
- String 内部由 byte[] value + byte coder 构成,纯 Latin-1 字符串节省约 50% 空间
- 更小的对象体积意味着堆中字符串实例更轻量,StringTable 引用所指向的目标更紧凑
- GC 对堆中字符串对象的扫描与回收更高效,尤其在高 intern 频率场景下体现明显
验证方式:看 OOM 错误类型
运行相同 intern 压测代码,错误提示可反推位置:
- JDK 6:抛出 java.lang.OutOfMemoryError: PermGen space
- JDK 7/8/9+:抛出 java.lang.OutOfMemoryError: Java heap space











