深入理解JVM - 垃圾收集
1. 概述
垃圾收集器 Garbage Collection。
垃圾收集器需要完成的三件事
- 哪些記憶體需要回收
- 什麼時候回收
- 如何回收
只有Java堆和方法區需要考慮記憶體回收,程式計數器、虛擬機器棧、本地方法棧的記憶體分配和回收都具有確定性。
2. 如何判斷物件已死
1. 引用計數法 Reference Counting
在物件中新增一個引用計數器,每當被其他物件引用,計數器+1,引用失效則計數器-1,當計數器值為0時表名該物件不可能再被使用。
原理簡單、判定效率高,難以解決迴圈引用問題。
2. 可達性分析演算法 Reachability Analysis
通過一系列稱之為 GC Roots
引用鏈 Reference Chain
,如果某個物件到GC Roots
間沒有引用鏈相連,也即從GC Roots
到這個物件不可達,則證明該物件不可再被使用。
GC Roots
- 虛擬機器棧(棧幀中的區域性變量表)中引用的物件,如引數、區域性變數、臨時變數等。
- 本地方法棧中Native方法引用的物件
- 方法區中類靜態屬性引用的物件,如Java類的引用型靜態欄位
- 方法區中常量引用的物件,如字串常量池中的引用
- VM內部引用,如基本資料型別對應的Class物件,常駐異常物件(NPE、OOM),系統類載入器等
- 同步鎖/監視器鎖 synchronized 所持有的物件
- 反應 JVM 內部情況 JMXBean、JVMTI 中註冊的回撥、原生代碼快取等。
以上是固定 GC Roots
,根據使用者選擇的垃圾收集器及當前回收的記憶體區域不同,還可以有其他臨時性物件加入。如分代收集和區域性回收(Partial GC),針對區域性劇區域的回收,還需要考慮該區域內物件被其他區域物件引用。當然為了避免 GC Roots
的膨脹,都進行了各種優化處理。
3. 各種引用
強軟弱虛四種引用。強引用 Stringly Reference
:最傳統的引用,只要還可達,就不會被回收。軟引用 Soft Reference
:OOM前對這些引用進行回收,若記憶體還不足才會OOM。SoftReference 類弱引用 Weak Reference
虛引用 Phantom Reference
:虛引用的存在不影響物件本身的生存時間,只是在物件被回收時得到通知。PhantomReference 類。
4. 標記過程
要宣告一個物件 不可達,至少需要經歷兩次標記。
第一次判斷不可達,判斷 finalize 方法是否已重寫或是否已被 VM 呼叫過。若沒有,則放入 F-Queue 佇列,由 VM 自動建立的,低優先順序的 Finalizer 執行緒執行 finalize 方法。
第二次對F-Queue物件判斷是否可達,若不可達則此物件宣告死亡。
finalize 釋放資源的問題:執行時間不確定,若某物件 fianlize 執行緩慢甚至死迴圈,可能導致記憶體回收子系統的崩潰。
當物件不可達時,可在 finalize 中拯救自己一次。
5. 方法區回收
主要兩部分內容:廢棄的常量和不再使用的記憶體。
類不再使用
- 例項全部已被回收
- 載入該類的類載入器已被回收
- 該類的 Class 物件沒有被引用
-Xnoclassgc、-verbose:class、-XX:+TraceClassLoading、-XX:+TraceClassUnLoading