字串物件在 Java 應用中佔據驚人的記憶體比例——獨立 UTF-16 字元的 char[] 陣列往往消耗 30% 以上堆空間。這不僅因為字串是最常用的互動介面,更歸因於流行的 HTTP API 大量使用字串傳輸。Java 8 Update 20 引入的字串去重特性,配合 G1 收集器,為這一痛點提供了原生解決方案。
String 內部以 final char[] value 儲存資料,JVM 可安全操控底層陣列。去重邏輯如下:
String 物件時,計算其 char[] 雜湊值並建立弱引用映射String 指向共享 char[],另一個陣列因無引用被回收為控制開銷,JVM 內建優化:若某字串一段時間未發現重複,則暫停檢查(Age Threshold 機制)。
-Xmx256m -XX:+UseG1GC
public class LotsOfStrings {
private static final LinkedList<String> LOTS_OF_STRINGS = new LinkedList<>();
public static void main(String[] args) throws Exception {
int iteration = 0;
while (true) {
for (int i = 0; i < 100; i++) {
for (int j = 0; j < 1000; j++) {
LOTS_OF_STRINGS.add(new String("String " + j));
}
}
iteration++;
System.out.println("Survived Iteration: " + iteration);
Thread.sleep(100);
}
}
}
結果:第 30 次迭代拋出 OutOfMemoryError。
-Xmx256m -XX:+UseG1GC -XX:+UseStringDeduplication -XX:+PrintStringDeduplicationStatistics
結果:成功運行 50+ 次迭代,單次 GC 釋放 4.5 MB 記憶體,去重率 100%。
[GC concurrent-string-deduplication, 4658.2K->0.0B(4658.2K), avg 99.6%, 0.0165023 secs] [Inspected: 119538] [Hashed: 119538(100.0%)] [Deduplicated: 119538(100.0%) 4658.2K(100.0%)] [Young: 372(0.3%)] [Old: 119166(99.7%)] [Table] [Memory Usage: 81.1K] [Size: 2048, Load: 135.5%] [Queue] [Dropped: 0]
| 維度 | 字串去重 | 字串駐留 |
|---|---|---|
| 共享層級 | 僅共享 char[] 底層陣列 | 共享整個 String 實例 |
| 觸發時機 | G1 GC 併發階段異步處理 | 應用線程主動呼叫 intern() |
| 適用場景 | 無感知、全自動、含 XML Schema/URL/JAR 名等 | 需開發者明確調用、適用已知重複常量 |
| 運行時開銷 | GC 階段微小併發開銷 | 呼叫時即時計算,可能阻塞應用線程 |
-XX:+UseG1GC)-XX:+UseStringDeduplication 開啟,-XX:+PrintStringDeduplicationStatistics 觀察效果字串去重是 JVM 層面的「免費午餐」,對於字串密集型應用(解析 XML/JSON、處理日誌、Web 爬蟲等)效果顯著。建議在測試環境充分驗證後再上線生產。