1. 程式人生 > >JVM 中判斷物件是否 “存活” 的演算法 —— 可達性分析演算法

JVM 中判斷物件是否 “存活” 的演算法 —— 可達性分析演算法

在堆中,幾乎存放著所有的物件例項,那麼回收這些物件例項時,我們需要判斷哪些物件是 “已死” 可以回收的,哪些物件是 “存活” 不需要回收的,下面就來介紹一下 JVM 中如何判斷上述問題的。

基本思路

通過一系列的稱為“GC Roots”的物件作為起始點,從這些節點開始向下搜尋,搜尋所走過的路徑稱為引用鏈(Reference Chain),當一個物件到GC Roots沒有任何引用鏈相連時,則證明此物件是不可用的。
這裡寫圖片描述

哪些可以作為GC Roots

  • 虛擬機器棧(棧幀中的本地變量表)中引用的物件。
  • 方法區中類靜態屬性引用的物件。
  • 方法區中常量引用的物件。
  • 本地方法棧中JNI(即一般說的Native方法)引用的物件。

物件自我拯救

注意,當一個物件例項被標記為不可達物件時,並不是一定會被回收,只是將該物件新增到回收的列表中。
那當被新增到回收列表中時,如果將自身從該列表移除呢?也就是說將該物件重新變成有用的物件呢?
Java Object類提供了一個物件自我救贖的方法:

protected void finalize();//當垃圾回收器確定不存在對該物件的更多引用時,由物件的垃圾回收器呼叫此方法。

如果物件要在 finalize() 中成功拯救自己——只要重新與引用鏈上的任何一個物件建立關聯即可,譬如把自己(this關鍵字)賦值給某個類變數或者物件的成員變數。
但是注意,finalize()方法只會被呼叫一次!
我們來看一下下面的例子:

/**
 * 此程式碼演示了兩點: 
 * 1.物件可以在被GC時自我拯救。 
 * 2.這種自救的機會只有一次,因為一個物件的finalize()方法最多隻會被系統自動呼叫一次
 */
public class FinalizeEscapeGC {
    public static FinalizeEscapeGC SAVE_HOOK = null;

    public void isAlive() {
        System.out.println("yes,i am still alive :)");
    }

    @Override
    protected void
finalize() throws Throwable { super.finalize(); System.out.println("finalize mehtod executed!"); FinalizeEscapeGC.SAVE_HOOK = this; } public static void main(String[] args) throws Throwable { SAVE_HOOK = new FinalizeEscapeGC(); // 物件第一次成功拯救自己 SAVE_HOOK = null; System.gc(); // 因為finalize方法優先順序很低,所以暫停0.5秒以等待它 Thread.sleep(500); if (null != SAVE_HOOK) { SAVE_HOOK.isAlive(); } else { System.out.println("no,i am dead :("); } // 下面這段程式碼與上面的完全相同,但是這次自救卻失敗了 SAVE_HOOK = null; System.gc(); // 因為finalize方法優先順序很低,所以暫停0.5秒以等待它 Thread.sleep(500); if (null != SAVE_HOOK) { SAVE_HOOK.isAlive(); } else { System.out.println("no,i am dead :("); } } }

執行結果如下:

finalize mehtod executedyes,i am still alive :)
no,i am dead :(

本文的內容、圖片和程式碼參考自:《深入理解Java虛擬機器:JVM高階特性與最佳實踐》