string常量池内存溢出主要发生在jdk6及更早版本,因string.intern()高频调用导致永久代被撑爆;jdk7起移至堆、jdk8+由元空间管理,误用仍可能引发堆或metaspace溢出;排查需结合oom日志(含permgen/metaspace异常及intern堆栈)、jstat监控(permcap或mu飙升)与代码高频误用模式(分页、日志拼接、未去重枚举)交叉验证;修复应禁用循环intern,改用预加载去重+缓存查表。

String 常量池内存溢出主要发生在 JDK 6 及更早版本,核心原因是 String.intern() 被高频、无节制调用,导致永久代(PermGen)被撑爆;JDK 7 起常量池移至堆中,JDK 8+ 则由元空间(Metaspace)管理,但误用 intern 仍可能引发堆或 Metaspace 溢出。排查关键不在猜,而在日志、监控和代码模式三者交叉验证。
看 OOM 日志是否带典型线索
服务崩溃时,检查日志是否同时满足:
- 异常明确为
java.lang.OutOfMemoryError: PermGen space(JDK 6)或java.lang.OutOfMemoryError: Metaspace(JDK 8+) - 堆栈中出现
String.intern(Native Method),且其上层紧邻循环结构(如for、while、forEach)或批量处理逻辑
用 jstat 观察常量池使用趋势
服务尚未宕机但响应变慢时,执行:
- JDK 6:
jstat -gcpermcapacity <pid></pid>,若PermCap使用率数分钟内从 30% 飙升至 95%+ 且不回落,基本锁定 intern 泛滥 - JDK 8+:
jstat -gc <pid></pid>查看MCMN(Metaspace 容量最小值)、MC(当前容量)、MU(已使用量),持续上涨且 GC 不释放,需警惕
扫代码中高频误用模式
重点检查以下三类高危写法:
- 分页查询中,对每条记录字段无条件调用
rs.getString("status").intern() - 日志拼接或 SQL 构建时先拼串再 intern,例如
("UPDATE t SET v=" + value).intern() - 从字典表加载枚举值,未去重就逐行 intern,比如 10 万行含 5 种状态,却执行了 10 万次 intern
临时止血与长期修复策略
确认问题后:
- 临时缓解(仅限 JDK 6):加 JVM 参数
-XX:PermSize=128m -XX:MaxPermSize=384m,避免启动即满,但上限勿超 512m,否则 GC 效率恶化 - 根治方案:禁用循环内 intern;改用预加载白名单——启动时读取全部合法字符串 → 去重 → 逐一 intern 并缓存为
Map<string string></string>;运行时只查缓存,绝不动态生成后入池
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











