jdk 6 中 intern() 将字符串复制到永久代常量池并返回新地址;jdk 7+ 则在堆中查找并直接返回已有对象引用,基于 equals() 匹配,复用更高效。

排查 String.intern() 在 JDK 6 和 JDK 7+ 的行为差异,关键不是看代码写法是否一样,而是观察运行时对象引用关系、内存区域归属和 GC 表现。下面从实操角度给出可验证、可复现的排查路径。
看 == 比较结果是否变化
这是最直接的判断依据。用动态构造的字符串(非字面量)调用 intern() 后与原对象做 == 判断:
- JDK 6:new String("ab") + new String("c") → .intern() 返回的对象和原对象地址一定不同,== 恒为 false
- JDK 7+:同上操作,若该字符串此前未在常量池出现过,== 很可能为 true(前提是堆中对象未被 GC)
- 注意避开预加载字符串(如 "java"、"class" 等),它们在 JVM 启动时已进常量池,无论哪个版本 intern() 都只返回已有引用
查 JVM 内存参数与 OOM 类型
运行时异常类型能快速定位底层区域:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 抛出 java.lang.OutOfMemoryError: PermGen space → 基本确定是 JDK 6 或开启了 -XX:PermSize 的旧版 JDK 7(极少见)
- 抛出 java.lang.OutOfMemoryError: Java heap space 或老年代 GC 频繁 → 更倾向 JDK 7+,说明 intern() 正在往堆里存引用,推高堆压力
- 可通过 jstat -gc
观察 Metaspace(JDK 8+)或 Perm 区使用率,确认常量池所在区域
用 JVM 工具验证字符串归属位置
借助诊断工具确认对象实际内存分布:
- 用 jmap -histo:live
查看字符串实例数量,再结合 -dump:format=b,file=heap.hprof 抓堆快照 - 用 JProfiler 或 VisualVM 打开 dump 文件,筛选 String 对象,观察其 classloader 和内存区域标签:JDK 6 中 intern 出的对象会标为 “Perm Gen”;JDK 7+ 全部显示在 “Heap” 下
- 对同一字符串调用 intern() 前后,对比其 identity hash code(System.identityHashCode())是否改变:JDK 6 改变(新对象),JDK 7+ 不变(复用原对象)
检查 JDK 版本与启动参数组合
有些行为看似“版本不符”,实为配置导致:
- 确认真实 JDK 版本:java -version,注意区分 OpenJDK / Oracle / Alibaba Dragonwell 等实现
- JDK 7u40+ 默认启用 G1 GC,而 G1 对字符串常量池的处理更敏感;某些定制 JDK 可能回退常量池位置
- 留意是否设置了 -XX:StringTableSize=N,该参数影响 intern() 查表性能,在 JDK 7+ 中可调大缓解哈希冲突,JDK 6 中无效
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










