序言
终结方法(finalizer)通常是不可预测的,书上说也是比较危险的,一般情况下是不必要的。虽然在java9中提供了(cleaner)清除方法代替了终结方法,但是还是不可预测的,一般情况下不要使用。
什么是finalizer
上面说了一大串,那么到底什么是finalizer 呢?如下:
public class Elvis {private static Elvis elvis = null;@Overrideprotected 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);//trueelvis = 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");}@Overridepublic 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);}@Overridepublic 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");}}
则代码会打印出
inithelloclose
如果没有使用try-with-resource 那么cleaner 会回收资源吗
答案是可以的,看如下代码
public static void main(String[] args) throws Exception {new Elvis();System.gc();}
此时呢代码会打印出
initclose
为什么要加 System.gc() 这一点呢书中给出了说明。因为cleaner在 System.exit() 也就是程序退出期间不确保清除操作做是否会被调用。这也就是书中所说的不确定性。加入gc 是为了在他退出前确保垃圾已经被回收了。
总结
总之呢,在没有java9之前千万千万谨慎的去使用 finalizer,书上写了后果自负。
