跳转到主内容
websoft网络软件专家 - 深耕网络技术,打造实用软件!

如何解释 String.intern() 在 JDK 6 与 JDK 7 之后在堆与常量池位置变化带来的内存差异?

JDK 6 中 intern() 将字符串复制到永久代常量池并返回新地址;JDK 7+ 则在堆中查找并直接返回已有对象引用,基于 equals() 匹配,复用更高效。 String.intern() 的行为差异不是“参数变了”或“用法错了”,而是底层常量池所在内存区域彻底迁移:JDK 6 在永久代(PermGen),JDK 7+ 在堆(Heap)。这直接决定对象是否复用、GC 是否可回收、甚至会不会
OutOfMemoryError
。 为什么 JDK 6 调用 intern() 会复制字符串对象 JDK 6 的字符串常量池在永久代,和 Java 堆物理隔离。调用
intern()
时: 若常量池中没有该字符串,JVM 必须把整个字符串内容从堆拷贝一份到永久代(新分配内存) 返回的是永久代里那个副本的引用,和原堆对象必然不是同一地址(
==
为
false
) 永久代空间小且 GC 频率低,大量
intern()
容易触发
java.lang.OutOfMemoryError: PermGen space
为什么 JDK 7+ 调用 intern() 只存引用不复制内容 JDK 7 起常量池移到堆中,和普通对象共享同一片内存管理区域: 如果堆中已存在该字符串(比如由
StringBuilder.toString()
创建),
intern()
直接把它的引用记录进常量池并返回 此时
==
可能为
true
(前提是该字符串此前未被 intern 过,且堆中对象未被 GC) 不再有永久代拷贝开销,也避免了 PermGen OOM;但堆内存压力变大,需关注老年代占用和 GC 暂停时间 常见误判场景:为什么 "java" intern 后 == 仍为 false 这个现象和 JDK 版本无关,只和“首次出现”原则有关:
"java"
是 JVM 启动时就加载的类名、包名等字面量之一,早已在常量池中存在 哪怕你用
new String("java").intern()
,也只是返回池中已有引用,不会改变原对象地址 而
"计算机软件"
这种运行期动态拼接、之前从未出现过的字符串,才是
intern()
真正起作用的典型场景 实际使用中最容易忽略的一点
intern()
不是“让字符串变常量”的魔法开关。它只影响字符串对象的引用归属,不影响其内容不可变性;更关键的是:它无法保证常量池中的引用永远有效——堆中对象若被 GC 回收,而常量池又持有其引用,那这个引用就变成“弱可达”,后续行为取决于 JVM 实现细节。生产环境慎用,尤其不要对高频生成的短生命周期字符串反复
intern()
。

相关文章