可达性分析算法中不可达的对象,也并不是一定会被回收掉,他们在真正的被回收掉之前,至少会经历两次标记过程:
如果对象在执行可达性分析后发现没有引用链跟GC Roots相连接,那他将会第一次被标记,并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法,当对象没有覆盖finalize()方法或者已经执行过一次finalize()方法,虚拟机都将这两种情况视为没有必要执行。
如果对象被视为有必要执行finalize()方法,这个对象会被放在一个叫做F-Queue的队列中,标在稍后有虚拟机创建的,低优先级的Finalizer线程中去执行它。这里的执行是指虚拟机只会触发这个方法,单并不保证会等待这个方法执行结束,之所以这样做不是因为如果这个对象的finalize()方法执行缓慢,或者发生了死循环,将会导致队列中的其他对象永久的等待,甚至导致整个垃圾回收系统崩溃。之后GC将会对F-Queue中的对象进行第二次的小规模的标记,如果对象在finalize()方法中,跟引用链上的任何一个对象建立了关联,第二次标记的时候他就会被移除即将被回收的集合,如果没有跟引用链上的任何一个对象建立关联,那么这个时候他就真的被回收了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| /** * 此代码演示了两点: * 1.对象可以在被GC时自我拯救。 * 2.这种自救的机会只有一次,因为一个对象的finalize()方法最多只会被系统自动调用一次 * @author zzm */ public class FinalizeEscapeGC {
public static FinalizeEscapeGC SAVE_HOOK = null;
public void isAlive() { System.out.println("yes, i am still alive :)"); }
@Override protected void finalize() throws Throwable { super.finalize(); System.out.println("finalize mehtod executed!"); FinalizeEscapeGC.SAVE_HOOK = this; }
public static void main(String[] args) throws Throwable { SAVE_HOOK = new FinalizeEscapeGC();
//对象第一次成功拯救自己 SAVE_HOOK = null; System.gc(); // 因为Finalizer方法优先级很低,暂停0.5秒,以等待它 Thread.sleep(500); if (SAVE_HOOK != null) { SAVE_HOOK.isAlive(); } else { System.out.println("no, i am dead :("); }
// 下面这段代码与上面的完全相同,但是这次自救却失败了 SAVE_HOOK = null; System.gc(); // 因为Finalizer方法优先级很低,暂停0.5秒,以等待它 Thread.sleep(500); if (SAVE_HOOK != null) { SAVE_HOOK.isAlive(); } else { System.out.println("no, i am dead :("); } } }
|
之所以有一次拯救成功了,有一次失败了,是因为finalize()方法只能被执行一次。
并不推荐使用finalize()方法拯救对象,因为该方法代价高昂,不确定性大,关于有些地方提到的这个方法适合关闭外部资源的说法,则完全有些自我安慰,而且关闭外部资源完全可以用try-finally更好的实现。
上一篇:回收方法区
下一篇:四种引用