请丢弃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本质上成为了快速回收的阻碍者,可能导致你的对象经过多个垃圾收集周期才能被回收。

下面来看一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Finalization {
public static Finalization finalization;
@Override
protected void finalize(){
System.out.println("Finalized");
finalization = this;
}

public static void main(String[] args) {
Finalization f = new Finalization();
System.out.println("First print: " + f);
f = null;
System.gc();
try {
// 休息一段时间,让上面的垃圾回收线程执行完成
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("Second print: " + f);
System.out.println(f.finalization);
}
}

mark

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

打赏
  • © 2018-2020 changlin zou
    • Page View:
    • Unique Visitor:

请我喝杯咖啡吧~

支付宝
微信