低基数文本指取值范围极小的字符串,如状态码、性别、省份缩写等,实际唯一值仅几十至几百个,适合string.intern()复用底层char[]或引用以显著降低内存占用。
通过 string.intern() 在海量低基数文本场景下实现内存减半,核心在于复用重复字符串的底层 char[](java 7+)或引用(java 6),避免堆中大量冗余对象。但效果取决于数据特征和 jvm 配置,并非无脑调用就能减半——关键在“低基数”+“合理控制 intern 池容量”+“避免反模式”。
什么是低基数文本?为什么它适合 intern()
低基数指字符串取值范围极小,比如:状态码("SUCCESS", "FAILED", "PENDING")、性别("MALE", "FEMALE")、省份缩写("BJ", "SH", "GD")、HTTP 方法("GET", "POST")等。哪怕原始数据有千万条记录,实际唯一字符串可能仅几十或几百个。
这种场景下,每个唯一值只需一份 char[] 存储,其余全指向它——intern() 正是做这件事。
必须启用并调优字符串常量池
默认的字符串常量池(StringTable)大小有限(JDK8 默认 1009 个桶),高并发 intern 容易哈希冲突、扩容开销大,甚至拖慢吞吐。需显式增大:
- 启动参数加
-XX:StringTableSize=65536(建议 2^n,如 65536 或 131072) - 避免频繁扩容:池大小应 ≥ 预估唯一字符串数量 × 1.5
- JDK 7u40+ 支持该参数;JDK 9+ 默认更大,但仍建议显式设置
正确使用 intern() 的姿势
不是所有字符串都该 intern。错误用法反而增加 GC 压力:
- 只对明确低基数、高频复用的字段调用:例如日志中的 level 字段、订单中的 status 字段,而非用户昵称或地址
-
在解析/构建时立即 intern:比如 JSON 反序列化时,对 status 字符串立刻调用
status.intern(),不要等后续逻辑再处理 -
避免对动态拼接字符串 intern:如
(name + "_" + id).intern()会生成大量唯一字符串,迅速撑爆池 -
注意 null 安全:调用前判空,
str != null ? str.intern() : null
验证是否真的省了内存
不能只看“用了 intern 就省”,要实测:
- 用 JFR 或 VisualVM 采样堆直方图,对比 intern 前后
java.lang.String实例数和总 retained heap - 重点关注 char[] 的总占用:低基数下,char[] 数量应趋近于唯一字符串数,而非原始记录数
- 监控 StringTable 使用率:
jstat -gc <pid></pid>查看 CMC(Compressed Class Space)或 G1 下的元空间压力(JDK8+ StringTable 在 native memory,可用jcmd <pid> VM.native_memory summary</pid>看 internal 部分)











