Java中如何标记对象已经死亡
要真正宣告一个对象死亡,至少要经历两次标记过程。
- 第一次标记
在可达性分析后发现到GC Roots没有任何引用链相连时,被第一次标记;
并且进行一次筛选:此对象是否必要执行finalize()方法;
-
没有必要执行
没有必要执行的情况:- 对象没有覆盖finalize()方法;
- finalize()方法已经被JVM调用过;
这两种情况就可以认为对象已死,可以回收;
-
有必要执行
对有必要执行finalize()方法的对象,被放入F-Queue队列中;稍后在JVM自动建立、低优先级的Finalizer线程(可能多个线程)中触发这个方法;
- 第二次标记
GC将对F-Queue队列中的对象进行第二次小规模标记;
finalize()方法是对象逃脱死亡的最后一次机会:- 如果对象在其finalize()方法中重新与引用链上任何一个对象建立关联,第二次标记时会将其移出"即将回收"的集合;
- 如果对象没有,也可以认为对象已死,可以回收了;
- 一个对象的finalize()方法只会被系统自动调用一次,经过finalize()方法逃脱死亡的对象,第二次不会再调用;
这里有一段演示代码:
public class FinalizeEscapeGC { public static FinalizeEscapeGC SAVE_HOOK = null; public void isAlive() { System.out.println("yes, I am still alive"); } protected void finalize() throws Throwable { super.finalize(); System.out.println("finalize method executed!"); FinalizeEscapeGC.SAVE_HOOK = this; } public static void main(String[] args) throws InterruptedException { SAVE_HOOK = new FinalizeEscapeGC(); //对象第一次成功拯救自己 SAVE_HOOK = null; System.gc(); //因为finalize方法优先级很低,所有暂停0.5秒以等待它 Thread.sleep(500); if (SAVE_HOOK != null) { SAVE_HOOK.isAlive(); } else { System.out.println("no ,I am dead QAQ!"); } //----------------------- //以上代码与上面的完全相同,但这次自救却失败了!!! SAVE_HOOK = null; System.gc(); //因为finalize方法优先级很低,所有暂停0.5秒以等待它 Thread.sleep(500); if (SAVE_HOOK != null) { SAVE_HOOK.isAlive(); } else { System.out.println("no ,I am dead QAQ!"); } } }