jdk 7+中string.intern()存入常量池的是堆中字符串对象的引用而非副本,因常量池已移至堆内存,intern()仅在stringtable中登记映射关系:查无则存引用、查有则返引用,不复制内容,支持gc回收。

在 JDK 7 及以后,String.intern() 将字符串“入池”时,存入的是堆中已有字符串对象的引用,而不是复制一份新对象或完整内容。
为什么是引用,不是副本
字符串常量池(运行时常量池)在 JDK 7+ 已迁移至堆内存,底层用哈希表(StringTable)管理。intern() 的本质是登记映射关系:
- 检查池中是否已有 equals() 相等 的字符串引用;
- 若存在,直接返回池中那个引用(可能指向字面量,也可能指向之前 intern 过的堆对象);
- 若不存在,就把当前 String 对象本身的引用加入池的哈希表,不创建新字符串实例。
关键表现佐证
这个行为有明确可验证的现象:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 调用
new String("abc").intern()后,返回值与"abc"的 == 判断为 true —— 说明它们指向同一个堆对象; - 原
new String("abc")创建的对象仍存在于堆中,只是现在多了一个来自常量池的强引用; - 如果后续无其他引用指向该对象,GC 仍可回收它(前提是常量池里也不再引用它)。
和 JDK 6 的区别在哪
JDK 6 中常量池在永久代(PermGen),intern() 需要将字符串内容拷贝一份到永久代,导致内存开销大、易 OOM;JDK 7+ 改为只存引用,复用堆中已有实例,更轻量、更安全,也支持 GC 回收。
使用时必须注意的一点
intern() 返回的是池中引用,但不会自动修改原变量指向。必须显式赋值才能生效:
✘ 错误写法:String s = new String("city"); s.intern(); // 返回值被丢弃,s 仍指向堆对象✔ 正确写法:
String s = new String("city"); s = s.intern(); // s 现在指向常量池中的引用Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










