-xx:+usestringdeduplication是g1 gc特有的字符串去重机制,仅对已晋升老年代的string对象底层byte[]/char[]执行异步去重,通过共享底层数组节省堆内存15%–40%,需搭配-xx:+useg1gc启用并用-xx:+printstringdeduplicationstatistics验证效果。

-XX:+UseStringDeduplication 是 G1 垃圾收集器(G1 GC)特有的字符串去重机制,它不是在“老年代中自动去重”,而是在 Young GC 或 Mixed GC 过程中,对已晋升到老年代的 String 对象(确切说是其内部 char[] / byte[])进行后台去重扫描和引用合并。它不作用于新生代对象,也不直接修改字符串内容,而是通过 JVM 内部的 deduplication 线程,在 GC 后识别重复的底层字符数组并复用。
以下是你需要清楚掌握的关键点:
它实际做什么?
- 仅对 G1 GC 管理的堆中、已进入老年代的 String 对象的底层字节数组(JDK 9+ 是
byte[]+coder) 执行去重; - 不处理
String.intern()或用户自建字符串池; - 不影响字符串逻辑语义(
equals()、==行为不变),只节省底层byte[]内存; - 去重发生在 GC 后的“字符串去重阶段”,由独立的
StringDeduplication线程异步执行,不阻塞用户线程。
如何正确启用并生效?
- 必须搭配 G1 GC 使用:
-XX:+UseG1GC -XX:+UseStringDeduplication
- 默认只对老年代中满足条件的 String 对象触发(需经过至少一次 GC 晋升);
- 可选调优参数(非必须,但建议了解):
-XX:StringDeduplicationAgeThreshold=3 —— 控制对象需在老年代存活多少次 GC 后才参与去重(默认为 3,避免过早处理短命字符串);
-XX:+PrintStringDeduplicationStatistics —— 输出去重统计(如扫描数、去重数、节省字节数),用于验证效果。
它能省多少内存?要看数据特征
- 每成功去重 N 个相同内容的
String,就只保留 1 份byte[],其余String对象共享该数组; - 若平均每个字符串底层
byte[]占 1.5KB,10 万个重复字符串 → 理论最多节省约 146MB 堆空间; - 但实际收益取决于:
- 字符串内容重复率(日志 ID、HTTP header、JSON 字段值等高重复场景效果明显);
- 字符串生命周期(长期驻留老年代才可能被扫描到);
- G1 GC 触发频率(Mixed GC 越多,去重机会越多)。
它不能替代什么?要避免误解
- ❌ 不是“防 OOM 的银弹”:若字符串本身极少重复,或大量短生命周期字符串未晋升,去重几乎无收益,反而增加 CPU 开销(哈希计算、并发读写
byte[]元信息); - ❌ 不解决强引用池泄漏:它不维护任何字符串池,不引入新引用,因此不会导致内存泄漏;
- ❌ 不兼容 CMS、ZGC、Parallel GC:该参数仅对 G1 GC 有效,其他 GC 会忽略或报错。
怎么确认它真起作用?
开启统计后,GC 日志中会出现类似输出:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
[GC String Deduplication: 1248K -> 32K (1216K), 0.0021234 secs]
表示本次去重将 1248KB 的重复 byte[] 压缩为 32KB,净节省 1216KB。配合 jstat -gc 观察 M(Metaspace)和 CCS(Compressed Class Space)无变化,但老年代 OU(Used Old)增长变缓,即可佐证效果。
不复杂但容易忽略——它是个“静默优化”,必须结合真实数据特征和可观测性才能发挥价值。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










