序言

终结方法(finalizer)通常是不可预测的,书上说也是比较危险的,一般情况下是不必要的。虽然在java9中提供了(cleaner)清除方法代替了终结方法,但是还是不可预测的,一般情况下不要使用。

什么是finalizer

上面说了一大串,那么到底什么是finalizer 呢?如下:

  1. public class Elvis {
  2. private static Elvis elvis = null;
  3. @Override
  4. protected void finalize() throws Throwable {
  5. System.out.println("调用了finalize");
  6. elvis = this;
  7. }
  8. }

为什么说这种不建议使用呢? 是这样的 ,finallize() 方法是Object类的方法, 用于在类被GC回收时 做一些处理操作, 但是JVM并不能保证finalize() 方法一定被执行!!
由于finalize()方法的调用时机具有不确定性,从一个对象变得不可到达开始,到finalize()方法被执行,所花费的时间这段时间是任意长的。我们并不能依赖finalize()方法能及时的回收占用的资源,可能出现的情况是在我们耗尽资源之前,gc却仍未触发。

  1. public static void main(String[] args) throws InterruptedException {
  2. Elvis elvis = new Elvis();
  3. elvis = null;
  4. System.gc();
  5. Thread.sleep(1000);
  6. elvis = Elvis.elvis;
  7. System.out.println(elvis != null);//true
  8. elvis = null;
  9. System.gc();
  10. Thread.sleep(1000);
  11. System.out.println(elvis != null);//false
  12. }

比如这样 当第二次值为false的时候并没有触发finalize() 方法。
总结一下啊

  • finalize机制本身就是存在问题的。
  • finalize机制可能会导致性能问题,死锁和线程挂起。
  • finalize中的错误可能导致内存泄漏;如果不在需要时,也没有办法取消垃圾回收;并且没有指定不同执行finalize对象的执行顺序。此外,没有办法保证finlize的执行时间。

所以一般不建议使用finalize 方法。

什么是 cleaner

finalize 从java9 开始已就被废除,通常的做法是提供显示的close()方法供客户端手动调用。这时候呢我们只需要实现一个AutoCloseable的接口。然后呢借助java9中的 Cleaner 类来实现清除方法

  1. public class Elvis implements AutoCloseable {
  2. // 创建者模式创建对象
  3. private final static Cleaner CLEANER = Cleaner.create();
  4. static class State implements Runnable {
  5. // 清理对象 下面说
  6. State() {
  7. System.out.println("init");
  8. }
  9. @Override
  10. public void run() {
  11. System.out.println("close");
  12. }
  13. }
  14. private final State state;
  15. // clearner 中的接口 实现唯一的清理方法
  16. private final Cleaner.Cleanable cleanable;
  17. public Elvis() {
  18. super();
  19. this.state = new State();
  20. // 注册清理容器中 并且需要清理对象的引用
  21. this.cleanable = CLEANER.register(this, state);
  22. }
  23. @Override
  24. public void close() throws Exception {
  25. cleanable.clean(); //进行清理操作
  26. }
  27. }

State类最好别引用Elvis 类的实例,因为引用了可能会造成循环依赖,你懂这样会阻止 Elvis 实例被垃圾回收(回收不完了),所以呢State是必须是一个静态的嵌套类,因为非静态的嵌套类包含了对外层类的引用。然后就是这个State不建议使用lambda,因为lambda很容易去使用其他类的引用。

而实现AutoCloseable 的好处是可以使用 try-with-resource(java7开始支持)

  1. public static void main(String[] args) throws Exception {
  2. try (Elvis elvis = new Elvis()) {
  3. System.out.println("hello");
  4. }
  5. }

则代码会打印出

  1. init
  2. hello
  3. close

如果没有使用try-with-resource 那么cleaner 会回收资源吗
答案是可以的,看如下代码

  1. public static void main(String[] args) throws Exception {
  2. new Elvis();
  3. System.gc();
  4. }

此时呢代码会打印出

  1. init
  2. close

为什么要加 System.gc() 这一点呢书中给出了说明。因为cleaner在 System.exit() 也就是程序退出期间不确保清除操作做是否会被调用。这也就是书中所说的不确定性。加入gc 是为了在他退出前确保垃圾已经被回收了。

总结

总之呢,在没有java9之前千万千万谨慎的去使用 finalizer,书上写了后果自负。