序言
终结方法(finalizer)通常是不可预测的,书上说也是比较危险的,一般情况下是不必要的。虽然在java9中提供了(cleaner)清除方法代替了终结方法,但是还是不可预测的,一般情况下不要使用。
什么是finalizer
上面说了一大串,那么到底什么是finalizer 呢?如下:
public class Elvis {
private static Elvis elvis = null;
@Override
protected void finalize() throws Throwable {
System.out.println("调用了finalize");
elvis = this;
}
}
为什么说这种不建议使用呢? 是这样的 ,finallize() 方法是Object类的方法, 用于在类被GC回收时 做一些处理操作, 但是JVM并不能保证finalize() 方法一定被执行!!
由于finalize()方法的调用时机具有不确定性,从一个对象变得不可到达开始,到finalize()方法被执行,所花费的时间这段时间是任意长的。我们并不能依赖finalize()方法能及时的回收占用的资源,可能出现的情况是在我们耗尽资源之前,gc却仍未触发。
public static void main(String[] args) throws InterruptedException {
Elvis elvis = new Elvis();
elvis = null;
System.gc();
Thread.sleep(1000);
elvis = Elvis.elvis;
System.out.println(elvis != null);//true
elvis = null;
System.gc();
Thread.sleep(1000);
System.out.println(elvis != null);//false
}
比如这样 当第二次值为false的时候并没有触发finalize() 方法。
总结一下啊
- finalize机制本身就是存在问题的。
- finalize机制可能会导致性能问题,死锁和线程挂起。
- finalize中的错误可能导致内存泄漏;如果不在需要时,也没有办法取消垃圾回收;并且没有指定不同执行finalize对象的执行顺序。此外,没有办法保证finlize的执行时间。
什么是 cleaner
finalize 从java9 开始已就被废除,通常的做法是提供显示的close()方法供客户端手动调用。这时候呢我们只需要实现一个AutoCloseable的接口。然后呢借助java9中的 Cleaner 类来实现清除方法
public class Elvis implements AutoCloseable {
// 创建者模式创建对象
private final static Cleaner CLEANER = Cleaner.create();
static class State implements Runnable {
// 清理对象 下面说
State() {
System.out.println("init");
}
@Override
public void run() {
System.out.println("close");
}
}
private final State state;
// clearner 中的接口 实现唯一的清理方法
private final Cleaner.Cleanable cleanable;
public Elvis() {
super();
this.state = new State();
// 注册清理容器中 并且需要清理对象的引用
this.cleanable = CLEANER.register(this, state);
}
@Override
public void close() throws Exception {
cleanable.clean(); //进行清理操作
}
}
State类最好别引用Elvis 类的实例,因为引用了可能会造成循环依赖,你懂这样会阻止 Elvis 实例被垃圾回收(回收不完了),所以呢State是必须是一个静态的嵌套类,因为非静态的嵌套类包含了对外层类的引用。然后就是这个State不建议使用lambda,因为lambda很容易去使用其他类的引用。
而实现AutoCloseable 的好处是可以使用 try-with-resource(java7开始支持)
public static void main(String[] args) throws Exception {
try (Elvis elvis = new Elvis()) {
System.out.println("hello");
}
}
则代码会打印出
init
hello
close
如果没有使用try-with-resource 那么cleaner 会回收资源吗
答案是可以的,看如下代码
public static void main(String[] args) throws Exception {
new Elvis();
System.gc();
}
此时呢代码会打印出
init
close
为什么要加 System.gc()
这一点呢书中给出了说明。因为cleaner在 System.exit()
也就是程序退出期间不确保清除操作做是否会被调用。这也就是书中所说的不确定性。加入gc 是为了在他退出前确保垃圾已经被回收了。
总结
总之呢,在没有java9之前千万千万谨慎的去使用 finalizer,书上写了后果自负。