字符串常量池位置决定intern行为:jdk6在永久代复制对象导致oom风险;jdk7起移至堆中仅存引用,支持gc回收且复用对象;jdk8+仍位于堆中,与元空间分离。

字符串常量池的位置决定 intern 行为
String.intern() 方法本身语义没变:查找并登记字符串,返回常量池中对应引用。真正影响效果的是字符串常量池(String Table)所在的内存区域——它从 JDK 6 的永久代,逐步迁移到堆中,这个物理位置变化直接改变了对象是否复制、能否被 GC 回收、以及 OOM 类型。
JDK 6:永久代里复制对象,容易爆内存
此时常量池属于方法区,由永久代(PermGen)实现:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 调用 intern() 时,若字符串不在池中,JVM 会把堆中字符串的**完整内容复制一份**到永久代
- 返回的是永久代副本的引用,和原堆对象地址不同,s == s.intern() 恒为 false
- 永久代空间小(默认几十 MB)、回收频率低(仅 Full GC),大量 intern 容易触发 java.lang.OutOfMemoryError: PermGen space
- StringTable 默认仅 1009 个桶,哈希冲突多,性能差
JDK 7 起:常量池进堆,只存引用不复制
从 JDK 7u40 开始稳定,字符串常量池整体迁移至 Java 堆:
- intern() 不再复制对象,而是将**堆中已有字符串的引用**登记进 StringTable
- 如果字面量 "hello" 已在编译期加载,new String("hello").intern() 返回的就是那个堆中对象,s == s.intern() 可能为 true
- 常量池随堆动态伸缩,OOM 错误变为 java.lang.OutOfMemoryError: Java heap space
- GC 可正常回收无强引用的 intern 字符串(但 StringTable 条目本身由 JVM 强持有,通常存活至退出)
- 默认桶数扩至 60013,可通过 -XX:StringTableSize 调整
JDK 8 及以后:元空间接管类元数据,字符串池仍在堆中
JDK 8 彻底移除永久代,引入本地内存的元空间(Metaspace)存放类信息;但字符串常量池并未随之迁移:
- 运行时常量池(符号引用等)进了元空间,字符串常量池仍严格位于堆中
- intern() 行为与 JDK 7 一致:查堆、存引用、复用对象
- OOM 错误类型是堆溢出,不是 Metaspace 溢出——这是验证位置最直接的依据
- 元空间适合存类结构,不适合高频增删的字符串场景,所以没把它挪过去
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










