字符串常量池(string interning)适用于高重复、长生命周期、取值有限的字符串,如状态码、配置键名、枚举标识等;应避免对uuid、时间戳等唯一性强的字符串调用,以防堆内存溢出。

直接利用字符串常量池(String Interning)做大规模字符串对比,核心是让相同内容的字符串指向同一内存地址,从而把耗时的 equals() 内容比对,降级为极快的 == 地址比对。
明确哪些字符串适合 intern
不是所有字符串都该调用 intern()。它真正适合的是:
- 重复率高、生命周期长的字符串,比如状态码("SUCCESS"、"FAILED")、配置键名("timeout.ms"、"bootstrap.servers")、枚举标识("US", "CN", "JP")
- 从外部稳定源读入的字符串,如数据库字典表字段、JSON 中固定字段名、日志中的固定事件类型
- 已知取值范围有限(几十到几千种),但实例数量极大(百万级以上)的场景
动态生成、唯一性强或仅用一次的字符串(如 UUID、时间戳拼接串、用户输入原文)不适合 intern,否则会撑爆堆内存且拖慢 GC。
正确使用 intern() 的写法
避免常见误用:
- 不要对构造器创建的字符串盲目调用
intern():例如new String("abc").intern()多此一举;直接写"abc"就已进池 - 推荐在字符串确定不可变、且需高频比较前统一 intern:
String key = input.trim().toLowerCase().intern(); - 注意 JDK 7+ 后
intern()操作的是堆内字符串常量池,无需担心 PermGen 溢出,但仍要监控堆中字符串池大小
配合 == 替代 equals() 进行批量比对
一旦完成 intern,后续比较就可安全用 ==:
- 比如判断请求类型:
if (method == GET || method == POST),前提是GET和POST是 intern 后的引用 - 在 Map 查找中,若 key 已 intern,
map.get(input.intern())比map.get(input)更快(尤其 key 集合大时) - 注意 null 安全:
str != null && str == TARGET,不能直接str == TARGET(否则空指针)
警惕 intern 的开销与替代方案
intern() 本身有同步开销,首次调用可能阻塞。高频并发场景建议:
- 预热:启动时将已知枚举值、配置项提前
intern()一遍 - 用
ConcurrentHashMap<string string></string>自建轻量池(putIfAbsent + 引用返回),规避 JVM 全局锁 - 对超大规模去重,考虑布隆过滤器(Bloom Filter)+ 哈希码快速筛除,再进精确比对










