StringDeduplication通过G1 GC后台线程异步去重老年代重复字符串的char[]数组,显著降低堆内存占用与GC压力;需显式启用-XX:+UseStringDeduplication并用日志验证效果。

直接用 JVM 参数开关 + GC 日志对比,是最有说服力的方式。面试官不关心你写了多少行 dedup 逻辑,只关心它是否真实减少了堆内存占用、是否降低了 GC 压力。
1. 启用并确认 StringDeduplication 生效
Java 8u20+ 默认关闭该功能,必须显式开启:
- -XX:+UseG1GC(StringDeduplication 仅 G1 支持)
- -XX:+UseStringDeduplication(核心开关)
- -XX:+PrintStringDeduplicationStatistics(打印去重统计,面试时必加)
运行后看 stdout 是否输出类似:
[String Deduplication: 2422K->128K (2294K)]
说明已成功将 2.4MB 重复字符串压缩为 128KB,节省超 2MB —— 这就是堆空间“被压榨”的第一手证据。
2. 对比堆内存使用量(关键证据)
写一个稳定复现重复字符串的测试程序(比如反复 substring 或 intern 大量相同内容),分别跑两组:
- A 组:不启用
-XX:+UseStringDeduplication - B 组:启用 + 相同负载 + 相同运行时长
用 -Xlog:gc+heap=debug 或 jstat -gc <pid> 抓取老年代(Old Gen)和整个堆(Heap)的 used 值。通常 B 组老年代占用下降 10%~40%,尤其在文本处理、日志解析、JSON 解析等场景效果显著。
3. 关联 GC 行为变化(间接但有力)
StringDeduplication 不减少对象数量,但减少实际堆字节,从而延缓老年代填满速度:
- 启用后,Full GC 次数明显减少(尤其在长时间运行服务中)
- G1 的 Mixed GC 触发频率降低,因为老年代存活数据变少
- 通过 -Xlog:gc*=info 查看 GC 日志中
old gen used和mixed gc行数变化
例如:某服务开启前每小时 Mixed GC 12 次,开启后降至 7 次 —— 这不是“优化了代码”,而是堆空间被更紧凑地利用了。
4. 注意前提与边界(体现深度)
避免被反问“为什么我开了没效果?”—— 主动说明限制条件:
- 仅对 G1 中已晋升到老年代、且经过一次 Minor GC 仍存活的字符串生效
- 字符串内容需完全相等(char[] 字节一致),大小写、编码、null 结尾都影响匹配
- dedup 是后台线程异步执行,不会阻塞 Mutator,但也不会立刻释放内存
- 对堆外内存(如 DirectByteBuffer)、常量池中的字符串无效
真正压榨的是老年代中重复的字符串实例所占的 char[] 数组空间,不是“去重”这个动作本身省了 CPU。

















