String.intern()仅对明确重复、生命周期长、内容固定的字符串(如日志级别、状态码、HTTP方法、配置key)有效;需避免对字面量、唯一值或循环内无条件调用,JDK 7+更安全但仍有锁竞争,推荐优先使用ConcurrentHashMap等可控缓存替代。

String.intern() 能让内容相同的字符串共享同一份堆内存,但不是“调了就省”,关键在用对场景、避开陷阱。
哪些字符串适合 intern
只对明确重复、生命周期长、内容固定的字符串使用:
- 日志级别:如 "INFO"、"WARN"、"ERROR"
- 状态码:如 "SUCCESS"、"FAILED"、"PENDING"
- HTTP 方法:如 "GET"、"POST"、"PUT"
- 配置项 key:如 "timeout", "retry.count", "env.profile"
这些值通常来自 JSON 解析、数据库查询或网络响应,原始创建方式常是 new String() 或 substring(),容易产生大量重复对象。
怎么写才真正节省内存
核心原则:只对非字面量、且确认会重复的字符串调用 intern。
立即学习“Java免费学习笔记(深入)”;
- 避免对字面量再 intern:如
"abc".intern()没意义,它本身已在常量池 - 避免对唯一值 intern:如 UUID、毫秒级时间戳、随机 token,几乎不会重复,反而占常量池空间
- 避免在循环里无条件调用:即使内容重复,每次都要查哈希表+加锁,性能损耗明显
- 推荐写法示例:
if (status != null) {
status = status.intern(); // 只对实际读取的字符串做处理
}
JDK 版本决定 intern 的行为逻辑
不同版本底层动作不同,直接影响效果:
- JDK 6 及之前:常量池在永久代,intern 会复制字符串内容到 PermGen,易 OOM
- JDK 7/8/9/10/11/12/13/14/15/16:常量池移到堆中,intern 不复制对象,只存引用,更安全
- JDK 17+(ZGC/Shenandoah):StringTable 在堆中,GC 可回收无强引用的 intern 条目,降低泄漏风险
因此 JDK 7+ 是使用 intern 的合理起点,但需注意:即使不复制,它仍占用堆内存,且 StringTable 是全局 synchronized 结构,高并发下有竞争开销。
比 intern 更可控的替代方案
对于需要精细控制缓存生命周期的场景,建议优先用显式缓存:
- 用
ConcurrentHashMap<String, String>自建缓存,可设过期策略、LRU 驱逐 - 配合弱引用(
WeakReference)或软引用,让 GC 在内存紧张时自动清理 - 对高频固定值(如枚举名),直接定义为
public static final String常量
intern 是 JVM 提供的全局机制,而 ConcurrentHashMap 是你可控的局部优化,更灵活、更易监控、更少副作用。


















