Java虛擬機:怎麼肯定對象已經死了?

怎麼肯定對象已經死了?java

怎麼肯定對象已經死了?怎麼肯定一個對象已經死了?算法

引用計數算法

給對象中添加一個引用計數器,每當有個地方引用它,計數器值就加1,引用失效,計數器減1,任什麼時候刻計數器爲0的對象就不能再應用了。ide

很難解決對象之間的相互循環引用。oop

引用計數收集器能夠很快的執行,而且交織在程序運行中,對程序須要不被長時間打斷的實時環境比較有利,但其很難解決對象之間相互循環引用的問題。以下面的程序和示意圖所示,對象objA和objB之間的引用計數永遠不可能爲 0,那麼這兩個對象就永遠不能被回收。
Java虛擬機:怎麼肯定對象已經死了?this

public class ReferenceCountingGC {
  
        public Object instance = null;

        public static void testGC(){

            ReferenceCountingGC objA = new ReferenceCountingGC ();
            ReferenceCountingGC objB = new ReferenceCountingGC ();

            // 對象之間相互循環引用,對象objA和objB之間的引用計數永遠不可能爲 0
            objB.instance = objA;
            objA.instance = objB;

            objA = null;
            objB = null;

            System.gc();
    }

上述代碼最後面兩句將objA和objB賦值爲null,也就是說objA和objB指向的對象已經不可能再被訪問,可是因爲它們互相引用對方,致使它們的引用計數器都不爲 0,那麼垃圾收集器就永遠不會回收它們。線程

可達性分析算法

經過一系列「GC roots」的對象做爲起始點,從這些節點開始向下搜索,搜索所走過的路徑成爲引用鏈(refenecre chain) ,當一個對象到GcRoot 沒有任何的引用鏈,則證實此對象不可用。code

Gcroot對象包括:對象

虛擬機棧(棧幀中的本地變量表) 中引用的對象。
方法區中類靜態屬性引用的對象
方法區中常量引用的對象
本地方法棧中JNI(native方法)引用的對象。
Java虛擬機:怎麼肯定對象已經死了?blog

生存仍是死亡

即時當一個對象不可達到,也並不是非死不可,這時它們處於一個緩行的階段要真正判處一個對象死亡,至少要經歷2次標記。第一次標記爲不可達到。當對象沒有覆蓋finalize() 方法,或者finalize方法已經被虛擬機調用了,虛擬機都被視爲不必執行(不必執行是否是直接回收?)隊列

若是一個對象被斷定有必要執行,則將這個對象放在一個F-Queue的隊列中,並由一個線程去執行它。finalize方法是對象逃脫死亡命運的最後機會。當在finalize方法中從新將之間賦值給了某個變量,那麼第二次標記就會被移除。若是對象第二次尚未逃脫,那麼基本就被回收了。

public class FinalizeEscapeGc {

    private static FinalizeEscapeGc instance = null;

    public void alive() {
        System.out.println("i'm alive");
    }

    /**
     * 該方法只調用一次
     * @throws Throwable
     */
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize()");
        FinalizeEscapeGc.instance = this;
    }

    public static void main(String[] args) throws InterruptedException {
        instance = new FinalizeEscapeGc();
        instance = null;
        System.gc();

        Thread.sleep(500);// finalize() method has a low priority to excute
        if (instance != null) {
            instance.alive();
        } else {
            System.out.println("ooops,i'm dead");
        }

        instance = null;
        System.gc();

        Thread.sleep(500);

        if (instance != null) {
            instance.alive();
        } else {
            System.out.println("ooops,i'm dead");
        }
    }
}

輸出:

finalize()

i’m alive

ooops,i’m dead

回收方法區

方法區在hotspot虛擬機稱爲永久代,永久代的垃圾收集主要回收兩部份內容:廢棄常量和無用的類。回收廢棄常量與回收堆相似,當一個字符串「abc」,進入了常量池,且沒有任何String對象叫「abc」,那麼它將會被回收。

判斷一個類爲無用的類:

該類的全部實例都被回收加載該類的classloader已經被回收該類對應java.lang.class對象沒有在任何地方引用,沒法經過反射訪問該類。

相關文章
相關標籤/搜索