首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java 垃圾回收器核心機制:對象存活判定、回收演算法與 finalize 陷阱

     編輯:Java 綜合教學

GC 存在的唯一目的

自動回收不再使用的 Java 對象佔用的堆內存。僅針對 new 建立、分配在堆上的對象。

非堆內存怎麼辦?

JNI 呼叫 C/C++ 分配的內存、直接記憶體、Metaspace 等,**GC 不管**。需手動釋放或依賴 Cleaner/PhantomReference 輔助。

finalize():最後一道防線(其實很脆弱)

  • GC 回收前**會嘗試**調用 finalize()
  • 可在其中釋放原生資源(如 free())
  • 缺陷:不保證執行時間、不保證執行順序、異常會中斷最終化、嚴重拖慢 GC、Java 9 已標記棄用
  • 替代方案:try-with-resources、AutoCloseable、Cleaner (Java 9+)

GC 不保證發生

內存充足時 JVM 可能**根本不觸發 GC**。不可在業務邏輯依賴 GC 行為(如鎖釋放、資源清理)。

對象存活判定:兩大演算法

1. 引用計數法(未採用)

每個對象有計數器,引用 +1,失去引用 -1,歸零即可回收。
致命缺陷:無法解決循環引用(A→B, B→A,計數器永不歸零)。

2. 根搜尋演算法(GC Roots Tracing)

主流商用 VM (HotSpot, JRockit, V8, CLR) 全部採用。
GC Roots 包含:

  • 虛擬機棧局部變數表引用的對象
  • 方法區類靜態屬性/常量引用的對象
  • 本地方法棧 JNI 引用的對象

過程:從 Roots 出發向下搜尋,走過的路徑 = 引用鏈。**無法到達 Roots 的對象 = 可回收**。完美解決循環引用。

GC Roots 可達性分析圖

經典回收演算法

演算法原理優點缺點應用
標記-清除標記存活 → 清除未標記實現簡單產生大量記憶體碎片老年代 (CMS 初期)
複製劃分兩塊,用完存活複製到另一塊,整塊清理無碎片、分配指針碰撞極快可用內存折半;存活多時複製開銷大新生代 (Eden + S0/S1)
標記-整理標記存活 → 向一端移動 → 清理邊界外無碎片、內存利用率高需移動對象、更新引用,停頓長老年代 (Parallel Old, G1 混合)
分代收集新生代用複製,老年代用標記-整理/標記-清除針對對象存活特性優化實現複雜所有商用 VM

HotSpot 具體實現:分代佈局

  • 新生代:Eden + From Survivor + To Survivor (默認 8:1:1)
  • 老年代:長期存活對象晉升
  • 元空間:類元數據 (Java 8+ 取代永久代)

Minor GC 清理新生代,Major/Full GC 清理老年代+元空間。

四大引用類型與 GC 關係

引用類型GC 行為典型用途
強引用永不回收普通對象 Object o = new Object()
軟引用內存不足前回收快取
弱引用下次 GC 回收緩存、ThreadLocalMap
虛引用任何時候可回收,需配合 ReferenceQueue直接記憶體釋放、Cleaner

總結

理解根搜尋判定存活、分代演算法權衡、finalize 不可靠、引用類型語義,是調優 GC、排查 OOM、設計快取的基石。實戰中優先選擇成熟收集器 (G1, ZGC, Shenandoah) 並配合業務特性調參,而非重造輪子。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved