Java review(15) finalize和gc

Java 有析構方法嗎?從客觀角度來說真的沒有。

但是有一個叫finalize的方法,字面意義它叫做“結束”方法,我不認爲它算析構方法,和C++比,它有一些顯著的特點:

  1. 它只在該對象被垃圾回收時纔會調用
  2. 它被調用了並不代表它就會被回收
  3. 它只會被調用一次

java的垃圾回收有一個顯著特點,那就是:在內存不足時,它纔開始回收。因此finalize什麼時候調用?這很難說。和C++不同,C++析構函數會立即起作用。

我們無法忽視我們並不能完全的控制內存。垃圾回收給了我們方便卻也限制了我們的自由。

 static class A {

        static A ALIVE = null;

        static void checkAlive() {
            System.out.println((ALIVE == null ? "I'm dead." : "I'm still alive!"));
        }

        @Override
        protected void finalize() throws Throwable {
            super.finalize();
            System.out.println("I'm finalised");
            ALIVE = new A();
        }
    }

    @Test
    public void test1() throws Exception {
        A a = new A();
        A.ALIVE = a;
        A.checkAlive();
        // alive

        System.gc();
        A.checkAlive();
        // alive 此時對象仍被a、A.ALIVE引用着

        a = null;
        System.gc();
        A.checkAlive();
        // alive 此時對象仍被A.ALIVE引用着

        A.ALIVE = null;
        System.gc();
        A.checkAlive();
        // alive 此時對象不再被引用,垃圾回收時執行了finalised方法,
        // A.ALIVE擁有了一個新對象,但是原來那個其實已經被回收了
        // 此處有一個地方值得注意:先輸出了I'm finalised,然後才輸出了I'm dead.
        // 這說明了垃圾回收的回收點應該在 System.out.println("I'm finalised"); 和 ALIVE = new A();之間
        // 反覆嘗試幾次會發現結果不同

        A.ALIVE = null;
        System.gc();
        A.checkAlive();
        // 此時回收點應該在ALIVE = new A();之後了
    }

輸出(代碼註釋分析的是這次輸出):

I'm still alive!
I'm still alive!
I'm still alive!
I'm finalised
I'm dead.
I'm finalised
I'm still alive!

反覆多次的結果:

I'm still alive!
I'm still alive!
I'm still alive!
I'm finalised
I'm still alive!
I'm finalised
I'm still alive!
I'm still alive!
I'm still alive!
I'm still alive!
I'm finalised
I'm still alive!
I'm dead.
I'm finalised

足以見得,垃圾回收是有隨機性的、不可控的

此外如果在finalize時,對象成功把自己綁定到一個外部的引用上,它便不會被回收,但是這種操作只有一次,因爲,對每一個對象,finalize只會執行一次

static class A {

        static A ALIVE = null;

        static void checkAlive() {
            System.out.println((ALIVE == null ? "I'm dead." : "I'm still alive!"));
        }

        @Override
        protected void finalize() throws Throwable {
            super.finalize();
            System.out.println("I'm finalised");
            ALIVE = this;
        }
    }


    @Test
    public void test1() throws Exception {
        A.ALIVE = new A();
        A.checkAlive();

        System.gc();
        A.checkAlive();

        System.gc();
        A.checkAlive();

        A.ALIVE = null;
        System.gc();
        A.checkAlive();
        A.checkAlive();
        A.checkAlive();
        A.checkAlive();

        A.ALIVE = null;
        System.gc();
        A.checkAlive();
        A.checkAlive();
        A.checkAlive();
        A.checkAlive();
    }

輸出:

I'm still alive!
I'm still alive!
I'm still alive!
I'm finalised
I'm still alive!
I'm still alive!
I'm still alive!
I'm still alive!
I'm dead.
I'm dead.
I'm dead.
I'm dead.

可以注意到幾點:

  1. gc只後使用了多個checkAlive,這是爲了防止回收點定的地方不準確導致結果不準確。gc的回收點設立是有一定依據的。
  2. 第一次finalize後,通過“ALIVE = this;”對象成功救活了自己
  3. 第二次gc finalize不再執行,對象被直接回收了,(finalize只執行一次)

部分代碼參考《深入JVM虛擬機》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章