search.png
关于我
menu.png
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虚拟机》

版权声明

知识共享许可协议 本文章由作者“衡于墨”创作,转载请注明出处,未经允许禁止用于商业用途

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。
发布时间:2019年11月29日 14:52:18

评论区#

还没有评论哦,期待您的评论!

关闭特效