避免 string.intern 内存溢出的关键是控制动态入池:jdk 7+ 堆中常量池、jdk 8+ 元空间均会因滥用 intern 导致 oom;需盯紧日志中 metaspace/堆溢出及 intern 调用栈,用 jstat 监控 mu 持续上涨,并整改分页逐条、拼接后、未去重加载三类误用;根治方案是启动时预加载去重字符串并缓存,运行时只查不调 intern。

避免 String.intern 造成内存溢出,核心是切断“无节制动态入池”这个泄漏源头。JDK 7+ 虽把常量池移到堆中,但 intern 仍会强引用字符串对象;JDK 8+ 移至元空间后,滥用仍会导致 Metaspace 溢出。关键不在禁用 intern,而在控制它何时、对什么、调用几次。
盯紧 OOM 日志里的两个信号
服务崩溃时,立刻检查日志是否同时出现:
-
异常类型明确为
java.lang.OutOfMemoryError: Metaspace(JDK 8+)或java.lang.OutOfMemoryError: Java heap space(堆溢出且伴生大量 String 实例) -
堆栈里有
String.intern(Native Method),且其上层方法明显在循环体、分页逻辑、批量解析或日志组装中
用 jstat 快速验证常量池增长趋势
服务尚未崩溃但响应变慢时,执行监控命令:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- JDK 8+:运行
jstat -gc <pid></pid>,重点关注MU(Metaspace 已使用量)是否持续上涨、GC 后不回落,且MC(当前容量)接近MCMN(最小容量) - 若发现
MU在几分钟内从 40% 涨到 90%+,基本可判定 intern 泛滥,不是 GC 问题,而是代码在疯狂注册新字符串
扫清三类高频误用代码模式
以下写法必须立即整改,它们是 intern 泄漏的“重灾区”:
-
分页/批量查询中逐条 intern 字段值:比如
rs.getString("code").intern()在 10 万行结果里执行 10 万次——实际只需 5 个唯一值 -
拼接后再 intern:如
("ERROR-" + errorCode).intern(),每次拼接都生成新字符串,再入池,完全失去去重意义 - 字典表加载未去重就 intern:从数据库查出全部状态枚举,没先用 Set 去重,直接 for 循环每行都 intern
用预加载白名单替代运行时 intern
这才是根治方案,既保留 intern 的性能优势,又杜绝泄漏:
- 应用启动时,一次性读取所有合法字符串(如状态码、类型名、配置键),用
Set去重 - 对去重后的每个字符串调用一次
intern(),并缓存到静态Map<string string></string>或枚举中 - 运行时所有业务逻辑只查缓存,绝不调用
xxx.intern()—— 即使传入的是新字符串,也返回已 intern 的引用
不复杂但容易忽略:intern 不是“优化手段”,而是“资源申请操作”。把它当成打开文件、建立连接一样对待——必须预判、必须管控、必须回收(此处“回收”即避免重复申请)。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










