java finalize方法总结、GC执行finalize的过程

原文地址:http://bijian1013.iteye.com/blog/2288223
一.finalize的作用

finalize()是Object的protected方法,子类可以覆盖该方法以实现资源清理工作,GC在回收对象之前调用该方法。
finalize()与C++中的析构函数不是对应的。C++中的析构函数调用的时机是确定的(对象离开作用域或delete掉),但Java中的finalize的调用具有不确定性。
不建议用finalize方法完成“非内存资源”的清理工作,但建议用于:
a.清理本地对象(通过JNI创建的对象);
b.作为确保某些非内存资源(如Socket、文件等)释放的一个补充:在finalize方法中显式调用其他资源释放方法。其原因可见下文[finalize的问题]

二.finalize的问题
一些与finalize相关的方法,由于一些致命的缺陷,已经被废弃了,如System.runFinalizersOnExit()方法、Runtime.runFinalizersOnExit()方法。
System.gc()与System.runFinalization()方法增加了finalize方法执行的机会,但不可盲目依赖它们。
Java语言规范并不保证finalize方法会被及时地执行、而且根本不会保证它们会被执行。
finalize方法可能会带来性能问题。因为JVM通常在单独的低优先级线程中完成finalize的执行。
对象再生问题:finalize方法中,可将待回收对象赋值给GC Roots可达的对象引用,从而达到对象再生的目的。
finalize方法至多由GC执行一次(用户当然可以手动调用对象的finalize方法,但并不影响GC对finalize的行为)。

三. finalize的执行过程(生命周期)

1.finalize大体流程

当对象变成(GC Roots)不可达时,GC会判断该对象是否覆盖了finalize方法,若未覆盖,则直接将其回收。否则,若对象未执行过finalize方法,将其放入F-Queue队列,由一低优先级线程执行该队列中对象的finalize方法。执行finalize方法完毕后,GC会再次判断该对象是否可达,若不可达,则进行回收,否则,对象“复活”。
2.具体的finalize流程
对象可由两种状态,涉及到两类状态空间,一是终结状态空间 F = {unfinalized, finalizable, finalized};二是可达状态空间 R = {reachable, finalizer-reachable, unreachable}。各状态含义如下:

a.unfinalized:新建对象会先进入此状态,GC并未准备执行其finalize方法,因为该对象是可达的。

b.finalizable:表示GC可对该对象执行finalize方法,GC已检测到该对象不可达。正如前面所述,GC通过F-Queue队列和一专用线程完成finalize的执行。
c.finalized:表示GC已经对该对象执行过finalize方法。
d.reachable:表示GC Roots引用可达。
e.finalizer-reachable(f-reachable):表示不是reachable,但可通过某个finalizable对象可达。
f.unreachable:对象不可通过上面两种途径可达
状态变迁图:
这里写图片描述

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

四.代码示例

1.对象复活

public class GC {  

    public static GC SAVE_HOOK = null;  

    public static void main(String[] args) throws InterruptedException {  
        SAVE_HOOK = new GC();  
        SAVE_HOOK = null;  
        System.gc();  
        Thread.sleep(500);  
        if (null != SAVE_HOOK) { //此时对象应该处于(reachable, finalized)状态  
            System.out.println("Yes , I am still alive");  
        } else {  
            System.out.println("No , I am dead");  
        }  
        SAVE_HOOK = null;  
        System.gc();  
        Thread.sleep(500);  
        if (null != SAVE_HOOK) {  
            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;  
    }  
}  
我们Debug上面代码,应该都是如下几种结果:

   结果一:
execute method finalize()  
Yes , I am still alive  
No , I am dead  
  结果二:
No , I am dead  
execute method finalize()  
Yes , I am still alive  
  结果三:
No , I am dead  
No , I am dead  
execute method finalize()  

因为System.gc();方法会启动一个守护线程执行finalize()方法,但由于线程调度随机,上面三种结果都有可能会出现,结果二即很好的展示了对象复活。

2.覆盖finalize方法以确保资源释放
作为一个补充操作,以防用户忘记“关闭”资源,JDK中FileInputStream、FileOutputStream、Connection类均用了此”技术“,下面代码摘自FileInputStream类。

/** 
 * Ensures that the <code>close</code> method of this file input stream is 
 * called when there are no more references to it. 
 * 
 * @exception  IOException  if an I/O error occurs. 
 * @see        java.io.FileInputStream#close() 
 */  
protected void finalize() throws IOException {  
    if ((fd != null) &&  (fd != FileDescriptor.in)) {  
        /* if fd is shared, the references in FileDescriptor 
         * will ensure that finalizer is only called when 
         * safe to do so. All references using the fd have 
         * become unreachable. We can call close() 
         */  
        close();  
    }  
}  

注:本文的目的并不是鼓励使用finalize方法,而是大致理清其作用、问题以及GC执行finalize的过程。
参考:

深入理解java的finalize http://zhang-xzhi-xjtu.iteye.com/blog/484934
The Finalizable Object http://www.artima.com/interfacedesign/Finalizable.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值