垃圾回收(GC)是 Java 自動內存管理的基石,也是面試高頻考點與性能調優核心戰場。本文系統梳理 GC 的「判定邏輯」、「收集演算法」、「分代策略」三大核心支柱,並結合 HotSpot 實現細節與生產調優視角,構建完整的 GC 知識體系。
不可達 ≠ 立即回收。對象必須經歷兩次標記才真正「死刑」:
finalize()(未重寫或已執行過則跳過)。finalize() 的對象放入隊列,低優先級 Finalizer 線程異步執行(不保證執行完成、不阻塞 GC)。finalize() 中重新與 GC Roots 建立關聯(如賦值給靜態字段) → 「復活」 → 移出回收集合。警示: finalize() 僅執行一次,第二次 GC 直接回收。不建議依賴它釋放資源,請用 try-with-resources / Cleaner (Java 9+)。
| 演算法 | 核心思想 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|---|
| 標記-清除 | 標記存活 → 清除死亡 | 實現簡單,無需移動對象 | 1. 效率低(雙遍歷) 2. 產生內存碎片,大對象分配困難 | 老年代(配合整理) |
| 複製 | 內存劃分兩塊等大區域,每次用一塊,GC 時存活對象複製到另一塊 | 無碎片,分配指針碰撞極快,實現簡單 | 浪費 50% 內存 | 新生代(存活率低) |
| 標記-整理 | 標記存活 → 移動存活對象到一端 → 清理邊界外內存 | 無碎片,內存利用率高 | 移動對象需更新引用,開銷大於清除 | 老年代(存活率高) |
→ 將堆劃分為新生代、老年代,針對不同特性選擇演算法。
-XX:SurvivorRatio=8)。-XX:PretenureSizeThreshold)。-XX:MaxTenuringThreshold)晉升老年代。-XX:MaxPermSize)。-XX:MetaspaceSize/MaxMetaspaceSize。| 收集器 | 代 | 演算法 | 線程 | 特點 | 適用場景 |
|---|---|---|---|---|---|
| Serial | 新/老 | 複製/標記-整理 | 單線程 | 簡單、Client 模式默認、STW | 單核、小內存、嵌入式 |
| ParNew | 新 | 複製 | 多線程 | Serial 多線程版、配合 CMS | 多核、需低停頓、配合 CMS |
| Parallel Scavenge | 新 | 複製 | 多線程 | 吞吐量優先、自適應調節 | 批處理、科學計算、後台任務 |
| Serial Old | 老 | 標記-整理 | 單線程 | Serial 老年代版、CMS 後備 | Client 模式、CMS 失敗後備 |
| Parallel Old | 老 | 標記-整理 | 多線程 | Parallel Scavenge 老年代版 | 吞吐量優先、多核 |
| CMS | 老 | 標記-清除 | 多線程 | 低停頓、並發收集、碎片化、CPU 敏感 | 響應時間敏感、Web 服務(JDK 9 棄用,JDK 14 移除) |
| G1 | 全堆 | 標記-整理+複製 | 多線程 | 分區、可預測停頓、Region、混合 GC、JDK 9+ 默認 | 大堆(>4G)、多核、均衡吞吐/停頓、現代首選 |
# JDK 8- -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log # JDK 9+ (統一日誌框架) -Xlog:gc*:file=gc.log:time,uptime,level,tags
-Xmn 或 -XX:SurvivorRatio。-XX:PretenureSizeThreshold 或優化代碼避免大數組/集合。-XX:MaxTenuringThreshold 或優化幸存區大小。-XX:+UseCMSCompactAtFullCollection)。JVM GC 是一套精密的工程權衡系統:可達性分析判生死,三大演算法權衡時空,分代假說指導區域劃分,收集器實現具體策略。理解其底層邏輯,而非死記參數,才能在面對 OOM、停頓抖動、吞吐下降等生產疑難雜症時,從容定位根因,精準施策。建議以 G1 為起點(JDK 11+ 默認),逐步探索 ZGC/Shenandoah 等新一代低延遲收集器,構建現代化 Java 服務的內存管理基石。