無法訪問已釋放的物件。_31、垃圾回收演算法 — 物件的finalization機制
技術標籤:無法訪問已釋放的物件。
01
物件的finalization機制
Java 語言提供了物件終止(finalization)機制來允許開發人員提供物件被銷燬之前的自定義處理邏輯。
當垃圾回收期發現沒有引用指向一個物件時,即:回收此物件之前,總會先呼叫這個物件的 finalize()方法
finalize()方法允許在子類中被重寫,用於在物件被回收時進行資源釋放。通常在這個方法中進行一些資源釋放和清理工作,比如關閉檔案、套接字和資料庫連線等。
永遠不要主動呼叫某個物件的 finalize()方法,應該交由垃圾回收機制呼叫,因為:
在 finalize()時可能會導致物件復活
finalize()方法的執行時間是沒有保障的,它完全由GC執行緒決定,並且優先順序很低,極端情況下,如果不發生GC,則finalize()將沒有執行機會
一個糟糕的finalize()會嚴重影響GC效能
如果從所有根節點都無法訪問到某個物件,說明物件已經不再使用。一般來說,此物件需要被回收。但是這個物件也不一定“非死不可”,它可能會復活自己。由於 finalize()方法的存在,虛擬機器中的物件一般處於三種可能狀態:
可觸及的:從根節點開始,可以到達這個物件
可復活的:物件所有的引用都被釋放,但是物件在 finalize()中復活
不可觸及的:物件的 finalize()被呼叫,並且沒有復活,就會進入此狀態。此物件一定會被回收。finalize()在垃圾回收之前只會呼叫一次。
判斷一個物件objA是否可以回收,至少要經歷兩次標記。
02
finalization機制具體過程
如果物件objA到GCRoots沒有引用鏈,就進行第一次標記
進行篩選,判斷此物件是否有必要執行finalize()方法
如果objA沒有重寫finalize() 方法,或者finalize() 已經被虛擬機器呼叫過,則虛擬機器視為“沒必要執行”,objA被判定為“不可觸及”狀態
如果物件objA重寫了finalize()方法,且還未執行過,那麼objA會被插入到F-Queue佇列中,由一個虛擬機器自動建立的、低優先順序的Finalizer執行緒觸發 finalize()方法執行
finalize() 方法是物件逃脫死亡的最後機會,稍後 GC 會對 F-Queue 佇列中的物件進行第二次標記。如果objA在 finalize()方法中與引用鏈上的任何一個物件建立了聯絡,那麼在第二次標記時,objA就會被移出“即將回收”的集合。之後,物件會再次出現沒有引用存在的情況。在這個情況下,finalize 方法不會被再次呼叫,物件會直接變成不可觸及狀態,也就是說,一個物件的 finalize() 方法自始至終只會被呼叫一次。
03
程式碼演示可復活的物件
不重寫 finalize()方法:
public class GCTest { public static GCTest obj; // 類變數,屬於 GC Roots public static void main(String[] args){ try { obj = new GCTest(); // 物件第一次拯救自己 obj = null; System.gc(); // 呼叫垃圾回收器 System.out.println("第一次 GC"); // 由於 Finalizer 執行緒優先順序很低,暫停2秒,等待Finalizer執行 Thread.sleep(2000); if (obj == null){ System.out.println("obj 物件已死"); }else{ System.out.println("obj 物件仍然存活"); } System.out.println("第二次 GC"); obj = null; System.gc(); // 呼叫垃圾回收器 // 由於 Finalizer 執行緒優先順序很低,暫停2秒,等待Finalizer執行 Thread.sleep(2000); if (obj == null){ System.out.println("obj 物件已死"); }else{ System.out.println("obj 物件仍然存活"); } }catch (Exception e){ e.printStackTrace(); } }}控制檯列印:第一次 GCobj 物件已死第二次 GCobj 物件已死
重寫 finalize()方法:
public class GCTest { public static GCTest obj; // 類變數,屬於 GC Roots @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("呼叫當前類重寫的 finalize() 方法"); obj = this; // obj 在呼叫 finalize() 時復活了自己 } public static void main(String[] args){ try { obj = new GCTest(); // 物件第一次拯救自己 obj = null; System.gc(); // 呼叫垃圾回收器 System.out.println("第一次 GC"); // 由於 Finalizer 執行緒優先順序很低,暫停2秒,等待Finalizer執行 Thread.sleep(2000); if (obj == null){ System.out.println("obj 物件已死"); }else{ System.out.println("obj 物件仍然存活"); } System.out.println("第二次 GC"); obj = null; System.gc(); // 呼叫垃圾回收器 // 由於 Finalizer 執行緒優先順序很低,暫停2秒,等待Finalizer執行 Thread.sleep(2000); if (obj == null){ System.out.println("obj 物件已死"); }else{ System.out.println("obj 物件仍然存活"); } }catch (Exception e){ e.printStackTrace(); } }}控制檯列印:第一次 GC呼叫當前類重寫的 finalize() 方法obj 物件仍然存活第二次 GCobj 物件已死
掃碼關注我
微訊號|fancheng1995
你們點點“分享”,給我充點兒電吧~