字符串常量池中的字符串可被GC回收,前提是其对象不可达——即无任何强引用链从GC Roots到达该字符串;JDK7+后池位于堆中,回收主要发生在Full GC或G1/ZGC等并发标记阶段,而非Minor GC。

字符串常量池里的字符串可以被 GC 回收,但不是主动清空,而是依赖 JVM 的可达性分析——只要池中某个字符串对象**没有强引用指向它**,在合适的 GC 阶段就可能被回收。
回收前提:字符串对象必须不可达
JDK7 及以后,字符串常量池位于堆内存中,池中存的是对字符串对象的引用,而字符串对象本身也分配在堆里。能否回收,只看这个字符串对象是否还被任何强引用持有:
- 局部变量、静态字段、集合容器、线程栈帧等仍持有该字符串的引用 → 不可回收
- 所有显式引用(如 String s = "abc")都被置为 null 或已出作用域,且无间接引用(比如没被缓存到 Map 里)→ 可能回收
- 类卸载后,其字面量字符串若未被其他类共享,也会因失去 Class 引用而变得不可达
实际触发回收的 GC 类型
字符串常量池不会被 Minor GC 扫描,它的清理主要发生在以下场景:
- Full GC:传统上最常见,会遍历整个堆和方法区(含常量池),做完整可达性分析
- G1 / ZGC / Shenandoah 的并发标记阶段:这些现代收集器在并发标记时也会检查常量池中的引用关系
- 元空间(Metaspace)配合类卸载时,若字符串仅由已卸载类持有,也会连带变为废弃常量
怎么判断一个字符串是不是“废弃常量”
标准很简单:池中那个字符串对象,在当前 JVM 中,是否还有任何一条从 GC Roots 出发的强引用链能到达它。
立即学习“Java免费学习笔记(深入)”;
- 例如 String s = "hello",只要 s 还在作用域内或被静态变量持有,“hello”就不可回收
- 而 new String("world").intern() 后,如果没其他变量引用该字符串,且后续所有持有者都释放了,它就满足废弃条件
- 注意:intern() 不等于“永久驻留”,只是把它放进池里;能不能留下,还是看有没有人用
辅助观察与调优手段
可以通过 JVM 参数了解常量池状态,避免盲目猜测:
- -XX:+PrintStringTableStatistics:每次 GC 后打印池大小、条目数、平均链长,看是否持续增长
- -XX:StringTableSize=60013:手动设哈希桶数量(建议质数),减少冲突,提升查找和清理效率
- jcmd <pid> VM.native_memory summary 或 jstat -gc <pid>:结合老年代使用率变化,间接推测大量字符串是否被回收


















