StringTable 垃圾回收/G1 去重
StringTable 的垃圾回收
StringTable 和普通对象一样,会被 GC 回收。当一个字符串常量不再被任何地方引用时,它就可以被 GC 回收。
StringTable 的 GC 时机
StringTable 的 GC 发生在Full GC时(JDK 8 及之前)或Metaspace GC时(JDK 9+)。
bash
# JDK 8:Full GC 时清理 StringTable
# JDK 9+:G1 GC 时清理 StringTable(JDK 12+ 专门优化)
# 开启 StringTable GC 日志
java -Xlog:stringtable*=trace MyAppStringTable GC 的日志
# JDK 8 Full GC 日志:
2026-03-22T10:00:00.123+0800: [Full GC (System.gc())
[StringTable清理]: before 12000, reclaimed 1500, 姑12500 entries]
# G1 GC 日志(JDK 12+):
[StringTable幽幽清理]: Cleaning% of 8600 used entries, reclaimed 5000 entries日志含义:before 12000 表示 GC 前有 12000 个 entry,reclaimed 1500 表示回收了 1500 个无效 entry。
StringTable GC 的原理
StringTable 是一个哈希表。GC 时,JVM 遍历 StringTable,对每个 entry 进行可达性分析:
java
// 简化逻辑:
for (String entry : stringTable) {
if (!entry.isReferenced()) {
// 没有任何地方引用这个字符串
// 可以从 StringTable 中移除
stringTable.remove(entry);
}
}关键点:一个字符串被回收的条件是没有任何地方引用它——不仅仅是 StringTable 内部没有重复引用,而是整个 Java 堆中都没有对它的引用。
java
public class StringTableGC {
public static void main(String[] args) {
String s = "temporary".intern();
// 此时 StringTable 有 "temporary" 的引用
// s 也持有引用
s = null; // s 不再引用
// 如果 GC 时发现没有其他地方引用 "temporary"
// "temporary" 就从 StringTable 中被回收
}
}影响 StringTable GC 的因素
1. StringTable 大小
StringTable 越大,能容纳的字符串越多,但 GC 时遍历时间越长。
bash
# JDK 8:StringTable 默认大小约 60013
java -XX:StringTableSize=100000 MyApp
# JDK 9+:StringTable 大小自动调整
# 但可以通过 -XX:StringTableSize 设置
java -XX:StringTableSize=1000000 MyApp2. GC 类型
不同的 GC 收集器对 StringTable 的处理策略不同:
| GC | StringTable GC |
|---|---|
| Serial / Parallel | Full GC 时清理 |
| CMS | 并发标记阶段不清理,Full GC 时清理 |
| G1 | JDK 12+ 在 cleanup 阶段并发清理 |
| ZGC / Shenandoah | 通过着色指针,几乎不影响停顿时间 |
G1 的字符串去重(JDK 12+)
JDK 12 引入了 G1 的字符串去重(String Deduplication) 功能。
什么是字符串去重
字符串去重和 StringTable 的概念不同:
| 概念 | 位置 | 机制 | 目的 |
|---|---|---|---|
| StringTable | StringTable 哈希表 | 字面量共享 | 节省内存 |
| String Deduplication | 堆中的字符串对象 | G1 识别并合并相同内容 | 节省更多内存 |
去重的原理
G1 的字符串去重会自动发现内容相同的字符串,让它们共享同一个 char[] / byte[]:
去重前(两个不同的 String 对象持有各自的 char[]):
String s1 = new String(new char[]{'h','e','l','l','o'});
String s2 = new String(new char[]{'h','e','l','l','o'});
│
│ G1 Deduplication 发现内容相同
▼
去重后(两个 String 对象共享同一个 char[]):
┌──────────────┐ ┌──────────────┐
│ String s1 │ │ String s2 │
│ value ──────────────── value │
│ (引用共享) │ │ (引用共享) │
└──────────────┘ └──────────────┘
│
▼
char[] {'h','e','l','l','o'}
(只有一个 char[] 数组)启用字符串去重
bash
# JDK 12+:启用字符串去重
java -XX:+UseG1GC \
-XX:+UseStringDeduplication \
MyApp
# 查看去重统计
java -XX:+UseG1GC \
-XX:+UseStringDeduplication \
-Xlog:stringdedup*=trace \
MyApp去重与 intern() 的区别
| 维度 | String.intern() | G1 String Deduplication |
|---|---|---|
| 触发方式 | 手动调用 .intern() | G1 自动识别并合并 |
| 对象 | 只处理 String | 处理堆中所有 String |
| 共享内容 | 整个 String 对象 | String 内部的 char[]/byte[] |
| 版本 | 所有 JDK | JDK 12+ G1 |
| 内存节省 | 节省 String 对象本身 | 节省 char[] 数组 |
去重的日志
[String Deduplication]
[Phase1: String referencing, 10.000ms]
[Phase2: Table scanning, 15.000ms]
[Concurrent String deduplication, 8000 strings candidates,
2000 strings deduplicated (50000 bytes saved)]实战:如何减少 StringTable 内存
1. 避免不必要的 intern()
java
// ❌ 不好:随机生成的字符串也 intern
String key = UUID.randomUUID().toString().intern();
// ✅ 好:只 intern 固定字符串
private static final String TYPE_USER = "user";
private static final String TYPE_ADMIN = "admin";2. 合理设置 StringTable 大小
bash
# 如果应用有大量字符串常量但 StringTable 过小
# 可能导致频繁的哈希冲突和 GC
java -XX:StringTableSize=2000000 MyApp3. 使用 G1 字符串去重
bash
# JDK 12+ 且使用 G1GC
java -XX:+UseG1GC -XX:+UseStringDeduplication MyApp4. FullGC 触发 StringTable GC
java
public class ForceStringTableGC {
public static void main(String[] args) {
// 通过 System.gc() 间接触发 Full GC,从而清理 StringTable
// 但这不推荐,因为 Full GC 停顿时间可能很长
System.gc();
}
}本节小结
StringTable 垃圾回收与 G1 去重的核心要点:
| 关键点 | 说明 |
|---|---|
| GC 时机 | Full GC(JDK 8-)/ G1 cleanup(JDK 12+) |
| 回收条件 | 没有任何地方引用该字符串 |
| 字符串去重 | JDK 12+ G1 的优化,合并内容相同的 String 的内部数组 |
| 与 intern() 的区别 | intern() 是手动去重 StringTable,dedup 是自动合并堆中 String |
| 调优参数 | -XX:StringTableSize、-XX:+UseStringDeduplication |
理解 StringTable 的 GC 机制,有助于在生产环境中诊断字符串相关的内存问题。
下一节,我们来看 new String() 创建对象数面试题,这是 String 相关最经典的面试题。
