finalize方法与GC
一、finalize()的作用
finalize() 是 Object 的 protected 方法,子类可以覆盖该方法以实现资源清理工作,GC在回收对象之前调用该方法。
finalize() 与 C++ 中的析构函数不是对应的。C++ 中的析构函数调用的时机是确定的(对象离开作用域或 delete 掉),但 Java 中的 finalize 的调用具有不确定性。
二、finalize()执行的生命周期
finalize流程:当对象变成 GC Roots 不可达时,GC 会判断该对象是否覆盖了 finalize 方法,若未覆盖,则直接将其回收。否则,若对象未执行过 finalize 方法,将其放入 F-Queue 队列,由一低优先级线程执行该队列中对象的 finalize 方法,执行 finalize 方法完毕后,GC 会再次判断该对象是否可达,若不可达,则进行回收,否则,对象“复活”。
对象可由两种状态,涉及到两类状态空间,一是终结状态空间 F = {unfinalized, finalizable, finalized};二是可达状态空间 R = {reachable, finalizer-reachable, unreachable}。各状态含义如下:
- unfinalized:新建对象会先进入此状态,GC 并未准备执行其 finalize 方法,因为该对象是可达的
- finalizable:表示 GC 可对该对象执行 finalize 方法,GC 已检测到该对象不可达
- finalized:表示 GC 已经对该对象执行过 finalize 方法
- reachable:表示 GC Roots 引用可达
- finalizer-reachable(f-reachable):表示不是reachable,但可通过某个finalizable对象可达
- unreachable:对象不可通过上面两种途径可达
具体状态转换:
- 新建对象首先处于[reachable, unfinalized]状态(A)
- 随着程序的运行,一些引用关系会消失,导致状态变迁,从reachable状态变迁到 f-reachable(B, C, D)或 unreachable(E, F)状态。
- 若 JVM 检测到处于 unfinalized 状态的对象变成 f-reachable 或 unreachable,JVM 会将其标记为 finalizable 状态(G,H)。若对象原处于[unreachable, unfinalized]状态,则同时将其标记为 f-reachable(H)。
- 在某个时刻,JVM 取出某个 finalizable 对象,将其标记为 finalized 并在某个线程中执行其finalize 方法。由于是在活动线程中引用了该对象,该对象将变迁到(reachable, finalized)状态(K或J)。该动作将影响某些其他对象从 f-reachable 状态重新回到 reachable 状态(L, M, N), 这就是对象重生。
- 处于 finalizable 状态的对象不能同时是 unreahable 的,由第4点可知,将对象 finalizable 对象标记为 finalized 时会由某个线程执行该对象的 finalize 方法,致使其变成 reachable。这也是图中只有八个状态点的原因。
- 程序员手动调用 finalize 方法并不会影响到上述内部标记的变化,因此 JVM 只会至多调用 finalize 一次,即使该对象“复活”也是如此。程序员手动调用多少次不影响 JVM 的行为。
- 若JVM检测到finalized状态的对象变成unreachable,回收其内存(I)。
- 若对象并未覆盖finalize方法,JVM会进行优化,直接回收对象(O)。
- System.runFinalizersOnExit()等方法可以使对象即使处于reachable状态,JVM仍对其执行finalize方法。
三、代码示例
public class GC {
public static GC SAVE_HOOK = null;
public static void main(String[] args) throws InterruptedException {
//新建对象,对象此时的状态是(reachable,unfinalized)
SAVE_HOOK = new GC();
//将SAVE_HOOK设置成null,此时刚才创建的对象就不可达了,因为没有句柄再指向它了,对象此时状态是(unreachable,unfinalized)
SAVE_HOOK = null;
//强制系统执行垃圾回收,系统发现刚才创建的对象处于unreachable状态,
//并检测到这个对象的类覆盖了finalize方法,因此把这个对象放入F-Queue队列,
//由低优先级线程执行它的finalize方法,
//此时对象的状态变成(unreachable, finalizable)或者是(finalizer-reachable,finalizable)
System.gc();
//sleep,目的是给低优先级线程从F-Queue队列取出对象并执行其finalize方法提供机会。
//在执行完对象的finalize方法中的super.finalize()时,对象的状态变成(unreachable,finalized)状态,
//但在覆盖的finalize方法中又执行了SAVE_HOOK = this;
//又有句柄指向这个对象了,对象又可达了。因此对象的状态又变成了(reachable, finalized)状态。
Thread.sleep(500);
if (null != SAVE_HOOK) { //此时对象应该处于(reachable, finalized)状态
//这句话会输出,注意对象由unreachable,经过finalize复活了。
System.out.println("Yes , I am still alive");
} else {
System.out.println("No , I am dead");
}
//再一次将SAVE_HOOK放空,此时刚才复活的对象,状态变成(unreachable,finalized)
SAVE_HOOK = null;
//再一次强制系统回收垃圾,此时系统发现对象不可达,虽然覆盖了finalize方法,但已经执行过了,因此直接回收。
System.gc();
//为系统回收垃圾提供时间
Thread.sleep(500);
if (null != SAVE_HOOK) { //此时对象应该处于(unreachable, finalized)状态
System.out.println("Yes , I am still alive");
} else {
//输出这句
System.out.println("No , I am dead");
}
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("execute method finalize()");
SAVE_HOOK = this;
}
}
输出结果:
execute method finalize()
Yes , I am still alive
No , I am dead