核心变化是字符串常量池从jdk 6的永久代迁移至jdk 7+的堆内存:jdk 6中intern()复制字符串到永久代并返回新地址,jdk 7+则直接存堆中对象引用,实现复用、避免oom。

核心变化是字符串常量池的位置迁移:JDK 6 在永久代(PermGen),JDK 7+ 移到了堆内存(Heap)。这导致 intern() 不再复制对象,而是直接复用堆中已存在的引用。
常量池所在区域彻底改变
JDK 6 的字符串常量池位于永久代,和 Java 堆物理隔离。调用 intern() 时,如果池中没有该字符串,JVM 会把整个字符串内容从堆拷贝一份到永久代,返回新地址。
JDK 7 起常量池被移到堆中,intern() 只做一件事:检查堆中是否已有内容相同的 String 实例;有就直接记录其引用并返回,不复制、不新建。
== 比较结果表现不同
- JDK 6:
new String("ab").intern() == new String("ab")恒为false,因为原对象在堆,intern()返回的是永久代副本 - JDK 7+:
new String("ab").intern() == new String("ab")可能为true,前提是该字符串此前未被intern过,且堆中对象未被 GC 回收 - 注意:像
"java"、"class"这类 JVM 启动时预加载的字符串,在任意版本下intern()都只返回已有引用,不能用来判断版本差异
内存异常类型可辅助判断
运行时抛出的 OOM 错误类型是快速识别 JDK 版本行为的线索:
-
java.lang.OutOfMemoryError: PermGen space→ 基本锁定为 JDK 6 或极早期 JDK 7 -
java.lang.OutOfMemoryError: Java heap space或老年代 GC 频繁 → 更倾向 JDK 7+,说明intern()正在往堆里存引用,推高堆压力
验证方法要结合工具和参数
单靠代码写法看不出区别,得看运行时实际行为:
- 加 JVM 参数
-XX:+PrintStringTableStatistics -XX:+UnlockDiagnosticVMOptions,启动后查看entries数量是否随业务增长缓慢——增长慢说明复用成功 - 用
jmap -dump:format=b,file=heap.hprof <pid></pid>抓堆快照,再用 VisualVM 或 JProfiler 打开,筛选String对象:JDK 6 中intern出的对象标为 “Perm Gen”,JDK 7+ 全部显示在 “Heap” 下 - 对比
System.identityHashCode():JDK 6 中调用intern()前后值通常改变(新对象),JDK 7+ 一般不变(复用原对象)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











