程式碼情境
LinkedList<String> list = new LinkedList<>();
list.add("a"); list.add("b");
list = new LinkedList<>(); // 重新指派
list.add("c"); list.add("d");
結論:舊鏈表**一定會被回收** (若無其他參考)
記憶體圖解
第一次 new LinkedList() → 物件 A (head→Node("a")→Node("b")→null)
list 參考 ──────────────────┘
list = new LinkedList() → 物件 B (head→Node("c")→Node("d")→null)
list 參考 ──────────────────┘
物件 A: 無任何參考指向 → GC Root 不可達 → 標記回收
關鍵點
- Java **達標式 GC**:從 GC Roots (棧區參考、靜態欄位、JNI 參考) 向下遍歷,**不可達** 物件即為垃圾。
- 節點間相互參考 (雙向鏈表
prev/next) **不影響** 可達性分析,只要鏈表頭節點不可達,整條鏈即不可達。
list = new ... 只是讓區域變數 list 指向新物件,舊物件瞬間失去最後一個 GC Root 參考。
驗證程式碼
public class GCDemo {
static class Node {
Node next;
String val;
Node(String v) { val = v; }
@Override protected void finalize() { System.out.println("Node " + val + " finalized"); }
}
public static void main(String[] args) {
Node head = new Node("a"); head.next = new Node("b");
head = new Node("c"); head.next = new Node("d"); // 丟棄 a-b 鏈
System.gc(); // 請求 GC (不保證立即執行)
Thread.sleep(1000);
}
}
// 輸出可能出現: Node a finalized / Node b finalized
最佳實務
- 不需手動將節點
next=null,現代 GC (G1, ZGC) 能高效處理循環參考。
- 大量短命物件 (如迴圈中 new List) 建議複用集合 (
list.clear()) 或調整新生代大小 (-Xmn)。
- 記憶體洩漏通常源於 **長生命週期物件持有短命物件參考** (如 static List 不斷 add),而非局部變數重新指派。