本題考察 String 物件在字串拼接、賦值 null 過程中的建立數量與最早可能觸發 GC 的程式碼行。理解字串常量池、堆上物件、引用變更對 GC 根可達性的影響是解題關鍵。
package Mar2017;
public class Ques0302 {
public static void main(String[] args) {
String str1 = new String("123"); // 第 1 個 String 物件(堆上)
String str2 = new String("123"); // 第 2 個 String 物件(堆上)
str1 = str1 + 1; // 第 3 行:產生新 String "1231",str1 指向新物件
str1 = null; // 第 4 行:str1 斷開引用,原 "1231" 成為垃圾
str2 = str2 + str1; // 第 5 行:str2 指向新 String "123null"
// 第 6 行
}
}
| 程式碼行 | 動作 | 堆上 String 物件狀態 | 可被 GC 物件 |
|---|---|---|---|
| 1 | new String("123") | 物件 A ("123") | — |
| 2 | new String("123") | 物件 B ("123") | — |
| 3 | str1 = str1 + 1 | 新物件 C ("1231"),str1→C;物件 A 無引用 | 物件 A 最早可被回收 |
| 4 | str1 = null | str1 斷開 C 引用 | 物件 C 可被回收 |
| 5 | str2 = str2 + str1 | 新物件 D ("123null"),str2→D;物件 B 無引用 | 物件 B 可被回收 |
StringBuilder 組裝後 toString() 產生新物件 C,str1 重新指向 C,導致物件 A 失去最後一個強引用,成為 GC 根不可達物件,最早可能觸發垃圾回收。str1 設為 null,釋放物件 C,比第 3 行晚。new String("123") 確保在堆上建立新物件,不使用常量池快取。第 3 行(str1 = str1 + 1;) 是垃圾回收器可能最先啟動回收的位置,回收對象為第一個 new String("123") 建立的物件 A。
String str = "123"; 直接使用常量池,new String("123") 強制堆上建立+ 本質是 StringBuilder.append().toString(),每次產生新物件System.gc() 僅建議,不保證立即執行嘗試畫出每一行執行後的引用圖,標記堆上物件與棧上引用變數的指向關係,養成「引用鏈分析」直覺,應對各類 GC 相關面試題。