Java 將內存管理權交給 JVM,開發者無需手動 delete,但理解 GC 原理對排查洩漏、調優延遲、設計高併發系統至關重要。本文系統梳理 GC 核心機制:對象何時可回收、何時真正回收、應用能否干預、終結器陷阱。
核心判定:可達性分析取代引用計數
早期引用計數法無法處理循環引用。現代 JVM 採用 GC Roots 可達性分析:
- GC Roots:棧帧局部變量、靜態屬性、常量、JNI 引用、同步鎖持有對象等。
- 從 Roots 出發搜索引用鏈,不可達對象判定為垃圾。
- 經過兩次標記(第一次標記不可達,第二次確認
finalize() 未復活)才真正回收。
觸發時機:JVM 自主決策,非應用可控
- Eden 區滿觸發 Minor GC
- 老年代空間不足觸發 Major/Full GC
System.gc() 僅是建議,JVM 可忽略(參數 -XX:+DisableExplicitGC 可禁用)。
- 實時/嵌入式系統可配置
-XX:+UseConcMarkSweepGC 等低延遲收集器,減少停頓。
分代收集:建立在弱分代假說之上
| 代 | 特徵 | 算法 | 典型區 |
| 新生代 | 朝生夕死,存活率低 | 複製算法 | Eden + 2 Survivor |
| 老年代 | 長期存活,大對象 | 標記-整理/標記-清除 | Tenured |
| 永久代/元空間 | 類元數據 | 較少收集 | Perm/Metaspace |
終結器 finalize():危險的「析構函數」
- 調用時機不確定:對象不可達 → 入終結隊列 → 專用線程執行 → 再次判定 → 回收。可能延遲極久甚至永不執行(JVM 退出前不保證執行)。
- 嚴禁在
finalize() 釋放關鍵資源(文件句柄、數據庫連接、鎖),會導致資源長期佔用、併發阻塞、數據不一致。
- 替代方案:
try-with-resources (JDK 7+)、AutoCloseable、PhantomReference + ReferenceQueue 實現確定性釋放。
常見洩漏模式與排查
- 靜態集合持有對象引用(緩存未清理)
- 監聽器/回調未註銷
- ThreadLocal 未
remove()(線程池場景)
- 內部類持有外部類引用(Handler、Runnable 常見)
工具鏈:jmap -histo、jhat、VisualVM、Eclipse MAT、JProfiler、-XX:+PrintGCDetails -Xloggc:gc.log。
總結
GC 非銀彈,理解其行為邊界、選擇合適收集器、編寫 GC 友好代碼(短命對象、避免大對象、及時切斷引用),才是高性能 Java 系統的內功心法。
- 上一篇文章: JBuilder 2007 開發 Web Services 全流程:從 WSDL 設計到客戶端生成
- 下一篇文章: 成為更優秀程序員的 10 個核心習慣:從防禦性編程到持續交付價值
Copyright ©
程式碼之家 All Rights Reserved