请丢弃finalize

用过JDK9的同学应该发现了,finalize方法在JDK9中已经被标记为deprecated,今天探讨一下finalize方法。如果没有特别的原因,不要实现finalize方法,也不要指望利用它来进行资源回收。因为你无法保证finalize什么时候执行,执行的是否符合预期。使用不当会影响性能,导致程序死锁、挂起等。

首先要明白的是为什么会出现finalize方法,因为早期一部分程序员是写C++的,仍然保留析构函数释放资源的行为,所以为了让他们平滑的过渡到Java并且适应Java,所以出现了finalize方法,用来对象被回收前的一次自我拯救,完成释放资源的操作。那么Object的finalize()方法的作用是否与C++的析构函数作用相同呢?其实finalize方法与C++的析构函数是有很大不同的,析构函数调用确定,而finalize是不确定的。重写了finalize的对象如果未被引用就会被放置于F-Queue队列,而且由一个优先级极底的线程成来执行finalize方法,而且方法执行随时可能会被终止。

finalize的执行是和垃圾收集关联在一起的,一实现了非空的finalize 方法,就会导致相应对象回收呈现数量级上的变慢,有人专做过benchmark,大概是40~ 50倍的下降。因为,finalize被设计成在对象被垃圾收集前调用,这就意味着实现了finalize方法的对象是个“特殊公民”, JVM要对它进行额外处理。finalize本质上成为了快速回收的阻碍者,可能导致你的对象经过多个垃圾收集周期才能被回收。

下面来看一个示例:

 1public class Finalization {
 2    public static Finalization finalization;
 3    @Override
 4    protected void finalize(){
 5        System.out.println("Finalized");
 6        finalization = this;
 7    }
 8
 9    public static void main(String[] args) {
10        Finalization f = new Finalization();
11        System.out.println("First print: " + f);
12        f = null;
13        System.gc();
14        try {
15            // 休息一段时间,让上面的垃圾回收线程执行完成
16            TimeUnit.SECONDS.sleep(1);
17        } catch (InterruptedException e){
18            e.printStackTrace();
19        }
20        System.out.println("Second print: " + f);
21        System.out.println(f.finalization);
22    }
23}

mark

可见finalize会拖慢垃圾收集,导致对象堆积,容易导致OOM,而且使用finalize释放资源是不合理的,资源应该是用完立即释放的,或者采用资源池来解决这个问题,而不是依靠被动垃圾回收时触发finalize方法。