垃圾回收 (GC) 是 Java 自動記憶體管理的基石。本文系統講解 GC 的判定演算法、主流收集器類型、JVM 實作演進,並給出調優關鍵點,幫助開發者理解 JVM 記憶體行為、排查 OOM 與長停頓問題。
每個物件維護一個計數器,引用+1,失效-1,歸零即可回收。優點:即時性強、停頓極短。致命缺陷:無法解決循環引用(A→B、B→A 計數永不歸零),早期 JVM 已棄用。
從 GC Roots(棧帧局部變數、靜態屬性、常數、JNI 引用等)出發,走對象圖標記可達物件。不可達者判定為垃圾。現代所有主流 JVM(HotSpot、OpenJ9、Zing 等)均採用此法。
| 演算法 | 核心思想 | 優點 | 缺點 |
|---|---|---|---|
| 標記-清除 | 標記可達 → 掃描堆釋放不可達 | 實作簡單 | 記憶體碎片化嚴重 |
| 標記-整理 | 標記後將存活物件向一端移動 | 無碎片 | 需移動物件、更新引用,耗時較長 |
| 複製 | 劃分 From/To 兩區,存活物件複製到 To 區 | 分配極快(指針碰撞)、無碎片 | 浪費 50% 空間;長壽命物件頻繁複製開銷大 |
基於「弱分代假說」:多數物件朝生夕死、少數長期存活。堆劃分為:
| 收集器 | 代 | 演算法 | 線程 | 特點 |
|---|---|---|---|---|
| Serial | 新/老 | 複製/標記-整理 | 單線程 | Client 模式預設,停頓長 |
| ParNew | 新 | 複製 | 多線程 | Serial 多線程版,配合 CMS |
| Parallel Scavenge | 新 | 複製 | 多線程 | 吞吐量優先,自適應調優 |
| CMS | 老 | 標記-清除 | 多線程 | 低停頓,碎片化、浮動垃圾 |
| G1 | 混合 | 標記-整理+複製 | 多線程 | 區域化、可預測停頓,JDK 9+ 預設 |
| ZGC / Shenandoah | 全堆 | 並發整理 | 多線程 | 亞毫秒級停頓,大堆(TB 級)首選 |
finalize() 僅執行一次,異常不阻礙回收,不建議依賴它釋放資源(改用 try-with-resources / Cleaner)。finalize() 中「復活」僅生效一次,下次 GC 直接回收。System.gc() 僅是建議,JVM 可忽略;生產環境避免顯式調用。
圖示:根節點可達性分析標記過程