0
点赞
收藏
分享

微信扫一扫

JAVA系列:finalize的作用

googlefrank 2022-03-12 阅读 58



[ faɪnəlaɪz ]

工作原理

一旦垃圾收集器准备好释放对象占用的存储空间,首先调用finalize(),而且只有在下一次垃圾收集过程中,才会真正回收对象的内存。finalize()方法的通用格式如下:

Java允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。Java语言规范中不仅不保证终结方法会被及时地执行,而且根本不保证他们会被执行。

如果使用了终结方法,就记住一定要调用super.finalize()。记住一句话:避免使用终结方法--finalize()。可以在finalize()让这个对象再次被引用,避免被GC回收;但是最常用的目的还是做cleanup


finalize工作流程

回顾一下,Finalizable对象的生命周期和普通对象的行为是完全不同的,列举如下:



  • JVM创建Finalizable对象
  • JVM创建 java.lang.ref.Finalizer实例,指向刚创建的对象。
  • java.lang.ref.Finalizer类持有新创建的java.lang.ref.Finalizer的实例。这使得下一次新生代GC无法回收这些对象。
  • 新生代GC无法清空Eden区,因此会将这些对象移到Survivor区或者老生代。
  • 垃圾回收器发现这些对象实现了finalize()方法。因为会把它们添加到java.lang.ref.Finalizer.ReferenceQueue队列中。
  • Finalizer线程会处理这个队列,将里面的对象逐个弹出,并调用它们的finalize()方法。
  • finalize()方法调用完后,Finalizer线程会将引用从Finalizer类中去掉,因此在下一轮GC中,这些对象就可以被回收了。
  • Finalizer线程会和我们的主线程进行竞争,不过由于它的优先级较低,获取到的CPU时间较少,因此它永远也赶不上主线程的步伐。
  • 程序消耗了所有的可用资源,最后抛出OutOfMemoryError异常。



带来的隐患

由于Finalizer线程优先级相较于普通线程优先级要低,而根据Java的抢占式线程调度策略,优先级越低的线程,分配CPU的机会越少,因此当多线程创建重写finalize方法的对象时,Finalizer可能无法及时执行finalize方法,Finalizer线程回收对象的速度小于创建对象的速度时,会造成F-Queue越来越大,JVM内存无法及时释放,造成频繁的Young GC,然后是Full GC,乃至最终的OutOfMemoryError。


为什么不能显示直接调用finalize方法?

  如前文所述,finalize方法在垃圾回收时一定会被执行,而如果在此之前显示执行的话,也就是说finalize会被执行两次以上,而在第一次资源已经被释放,那么在第二次释放资源时系统一定会报错,因此一般finalize方法的访问权限和父类保持一致,为protected。


举报

相关推荐

0 条评论