JDK 8u20 引入 String Deduplication,針對 Java 應用中佔堆 20-30% 的 String 物件,透過共享底層 char[]/byte[] 降低記憶體佔用。僅支援 G1 GC,預設關閉。
String 物件,計算其值陣列雜湊並存入弱參考表。String 的 value 欄位指向另一個的陣列。String 會被標記「冷卻」,一段時間不再檢查。-XX:+UseG1GC
-XX:+UseStringDeduplication
-XX:+PrintStringDeduplicationStatistics # 列印去重統計
public class StringDedupDemo {
private static final List HOLDER = new ArrayList<>();
public static void main(String[] args) throws Exception {
int iter = 0;
while (true) {
for (int i = 0; i < 100; i++)
for (int j = 0; j < 1000; j++)
HOLDER.add(new String("String " + j)); // 大量重複字串
System.out.println("Survived Iteration: " + ++iter);
Thread.sleep(100);
}
}
}
| 配置 | 存活輪數 | 備註 |
|---|---|---|
| G1 + 無去重 | ~30 | OOM 崩潰 |
| G1 + 啟用去重 | 持續運行 | 記憶體穩定,GC 日誌顯示去重量 |
[String Deduplication]
Scanned 123456 objects,
Deduplicated 110000 (89.1%),
Saved 45.2 MB (12.3% heap),
12.5 ms (avg 0.1 us/obj)
| 特性 | String Deduplication | String.intern() / 常量池 |
|---|---|---|
| 範圍 | 僅共享 value 陣列 | 共享整個 String 實例 |
| 觸發時機 | G1 GC 並發標記階段 | 呼叫 intern() 或編譯期常量 |
| 適用對象 | 動態產生、生命週期長的 String | 固定常量、手動驅動 |
| 記憶體節省 | 陣列大小 × 重複數 | 整個物件頭 + 陣列 |
Compact Strings (Latin-1/UTF-16) 已大幅降低單字串記憶體,去重效益遞減。Java 8u20+ 專案若使用 G1 GC 且觀察到大量重複 String 佔用堆,建議開啟 -XX:+UseStringDeduplication 並觀察 GC 日誌統計,若節省 > 10% 堆且停頓增長可接受,即可長期啟用。