Java中String不可变性使常量池成为自动复用机制,JDK7+池在堆中可GC回收;省内存关键在代码写法:优先字面量、禁用new String()、谨慎使用intern()。

Java 中不可变 String 的常量池本身不能通过配置“开启省内存”,它不是开关式功能,而是 JVM 基于 String 不可变性自动构建的复用机制。真正决定是否省内存的,是你如何写代码,以及是否理解常量池的运行逻辑和边界条件。
✅ 正确理解常量池的位置与回收能力
- JDK 7 及以后:字符串常量池在堆内存中,和普通对象一样可被 GC 回收(前提是无强引用)
- JDK 6 及以前:在永久代(PermGen),大小固定、几乎不回收,容易 OOM
-
元空间(Metaspace)不存字符串:别误配
-XX:MaxMetaspaceSize来“解决”字符串内存问题
所以,不是配参数让池“更省”,而是确保池里只放值得驻留的字符串。
✅ 哪些字符串值得进常量池?看三个硬条件
满足以下全部,才建议让它进池:
-
长期存活:如
"PENDING"、"database.url"、"HTTP/1.1",整个应用生命周期都存在 -
内容高度重复:比如日志级别
"INFO"在百万请求中出现几十万次 -
用作哈希结构 key:在
HashMap、ConcurrentHashMap中高频作为键,复用能减少equals()调用和哈希冲突
不满足任一条件的,比如:
立即学习“Java免费学习笔记(深入)”;
- 用户输入、URL 路径、JSON 字段名、循环内
StringBuilder.toString()结果
→ 绝不建议.intern(),反而推高 GC 压力
✅ 写法上直接省内存:优先字面量,禁用 new String()
- ✅ 推荐:
String method = "GET";→ 编译期入池,复用安全 - ❌ 避免:
String method = new String("GET");→ 堆中多建一个对象,绕过池,纯属冗余 - ⚠️ 警惕:
"GET".intern()对字面量无效(已驻留),是多余调用
new String("x").intern()先堆分配再查池,比直接"x"多一次对象创建和锁竞争。
✅ 运行期需要 intern?按场景谨慎使用
适用场景(数据库/配置读取的高复用值):
String status = rs.getString("status").intern();String currency = config.get("currency").intern();
但要注意:
- 避免在每请求都执行的 filter、interceptor 中无条件调用
- 若需批量 intern(如加载百万级词典),可用 JVM 参数扩容哈希桶:
-XX:StringTableSize=131072(默认 60013,减少 hash 冲突)
验证是否生效:
- 用
s1 == s2判断是否指向同一对象(比.equals()更直接) - 加
-XX:+PrintStringDeduplicationStatistics(JDK 8u20+)看去重统计
✅ JDK 9+ 自动启用紧凑字符串,无需配置
- 当字符串只含 Latin-1 字符(ASCII 0–127,如英文、数字、常见符号),内部用
byte[]存储 - 每字符占 1 字节(旧版
char[]占 2 字节),内存直接减半 - 完全透明:
length()、charAt()行为不变,开发者无感知
你只需确保字符串内容干净(不含中文、emoji 等),JVM 就会自动压缩。
不复杂但容易忽略


















