Java基础-03丨Object.finalize()

Posted by jiefang on November 27, 2019

Object.finalize()

简介

官方注释

Called by the garbage collector on an object when garbage collection determines that there are no more references to the object. A subclass overrides the {@code finalize} method to dispose of system resources or to perform other cleanup.

译文:当垃圾回收确定不再有对该对象的引用时,由垃圾回收器在对象上调用。 子类覆盖{@code finalize}方法以处置系统资源或执行其他清理。

当对象不可达时(GC Roots), GC会判断对象是否覆盖了finalize()

  • 若未覆盖则直接将其回收。
  • 若对象覆盖并且未执行过(手动调用的不计此列)finalize(), 将其放入F-Queue队列,由一个低优先级线程执行该队列中对象的finalize()方法。执行finalize()完毕后, Gc会再次判断该对象是否可达:
    • 若不可达, 则进行回收。
    • 可达, 对象“复活”。

原理

即使在可达性分析算法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段。要真正宣告一个对象死亡,至少要经历两次标记过程:如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”。

如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会放置在一个叫做F-Queue的队列之中,并在稍后由一个由虚拟机自动建立的、低优先级的Finalizer线程去执行它。这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束,这样做的原因是,如果一个对象在finalize()方法中执行缓慢,或者发生了死循环(更极端的情况),将很可能会导致F-Queue队列中其他对象永久处于等待,甚至导致整个内存回收系统崩溃。

finalize()方法是对象逃脱死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()中成功拯救自己——只要重新与引用链上的任何一个对象建立关联即可,譬如把自己(this关键字)赋值给某个类变量或者对象的成员变量,那在第二次标记时它将被移除出“即将回收”的集合;如果对象这时候还没有逃脱,那基本上它就真的被回收了。

对象生命流程

对象可由两种状态组成, 涉及到两类状态空间:

  • 终结状态空间:F = {unfinalized, finalizable, finalized};
  • 可达状态空间:R = {reachable, F(finalizer)-reachable, unreachable}。

  • unfinalized: 新建对象会先进入此状态,GC并未准备执行其finalize方法,该对象可达的;
  • finalizable: GC已检测到该对象不可达,GC通过F-Queue队列和专用线程完成finalize的执行;
  • finalized: GC已经对该对象执行过finalize方法;
  • reachable:GC Roots引用可达;
  • finalizer-reachable:不是reachable,但可通过某个finalizable对象可达;
  • unreachable:不可达,没有被任何对象引用;

状态变化图

  1. 新建对象处于reachable, unfinalized(A)状态;
  2. 随着程序的运行,一些引用关系会消失,导致状态变迁,从reachable状态变迁到f-reachable(B, C, D)或unreachable(E, F)状态;
  3. 若JVM检测到处于unfinalized状态的对象变成f-reachableunreachable,JVM会将其标记为finalizable(G,H)状态,若对象原处于[unreachable, unfinalized]状态,则同时将其标记为f-reachable(H);
  4. 在某个时刻,JVM取出某个finalizable对象,将其标记为finalized并在某个线程中执行其finalize()。由于是在活动线程中引用了该对象,该对象将变迁到(reachable, finalized)(K或J)状态,该动作将影响某些其他对象从f-reachable状态重新回到reachable(L, M, N)状态;
  5. 处于finalizable状态的对象不能同时是unreahable的,由第4点可知,将对象finalizable对象标记为finalized时会由某个线程执行该对象的finalize(),致使其变成reachable;
  6. 程序员手动调用finalize()并不会影响到上述内部标记的变化,因此JVM只会至多调用finalize()一次,即使该对象“复活”也是如此。
  7. JVM检测到finalized状态的对象变成unreachable,回收其内存(I);
  8. 若对象并未覆盖finalize(),JVM会进行优化,直接回收对象(O);
  9. System.runFinalizersOnExit()等方法可以使对象即使处于reachable状态,JVM仍对其执行finalize方法;