【查缺补漏】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:对象不可通过上面两种途径可达
    状态流转

具体状态转换:

  1. 新建对象首先处于[reachable, unfinalized]状态(A)
  2. 随着程序的运行,一些引用关系会消失,导致状态变迁,从reachable状态变迁到 f-reachable(B, C, D)或 unreachable(E, F)状态。
  3. 若 JVM 检测到处于 unfinalized 状态的对象变成 f-reachable 或 unreachable,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 一次,即使该对象“复活”也是如此。程序员手动调用多少次不影响 JVM 的行为。
  7. 若JVM检测到finalized状态的对象变成unreachable,回收其内存(I)。
  8. 若对象并未覆盖finalize方法,JVM会进行优化,直接回收对象(O)。
  9. 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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值