-
Java语言提供了对象终止(finalization)机制来允许开发人员提供对象被销毁之前的自定义处理逻辑。
-
当垃圾回收器发现没有引用指向一个对象,即:垃圾回收此对象之前,总会先调用这个对象的finalize() 方法。
-
finalize() 允许子类中被重写,用于对象被回收时进行资源释放。通常在这个方法中进行一些资源释放和清理的工作,比如关闭文件,套接字和数据库连接等。
-
Object类的方法
protected void finalize() throws Throwable { }
- 永远不要调用某个对象的finalize()方法。应该交给垃圾回收器调用,理由如下:
- 在finalize()时对象可能复活
- finalize()的执行时间是没有保障的,它完全由GC线程决定,极端情况下,如果永远不发生GC,那么finalize()方法永远不会被执行。
- 一个糟糕的finalize()会严重影响GC执行。
- 从功能上说,finalize()方法和c++的析构函数比较类似,但Java采用的是基于垃圾回收的自动管理机制,所以本质上不同于c++的析构函数。
- 由于finalize()的存在,虚拟机中对象一般处于三种状态。
如果所有的根节点无法访问到某个对象,说明该对象已经不再使用。一般来说此对象需要被回收。但事实上也并非是“非死不可”的,这时候他们暂时处于缓刑阶段。一个无法触及的对象可能在某一个条件下复活自己,如果那样,那么对它的回收就是不合理的,为此虚拟机中的对象有以下三个状态:
- 可触及的:从根节点可以达到的对象
- 可复活的:对象的多有引用都被释放,但是对象可能在finalize()中复活。
- 不可触及的:对象finalize()被调用,并且没有复活,那么就会进入不可触及状态。不可触及的对象不可复活,因为finalize()只会被调用一次。
-
以上三种状态中,是由于finalize()存在进行的区分,只有对象是不可及状态才会被回收。
-
判定一个对象是否可以回收至少要经过两次标记。
1:如果对象A到GC Roots没有引用链,则进行第一次标记。
2:进行筛选,判断此对象是否有必要执行finalize()方法。
(1)如果对象A没有重写finalize()方法,或者finalize()方法已经被虚拟机调用过,则虚拟机视为没有必要执行,A被视为不可执行的。
(2)如果对象A重写了finalize()方法,且还未执行过,那么A会被插入到F-Queue队列中,由一个虚拟机自动创建的,低优先级的Finalizer线程触发其finalize()方法执行。
(3)finalize()方法是对象逃脱死亡的最后方法,稍后GC会对F-Queue队列中的对象进行二次标记,如果A在finalize()中与引用链上的任何一个对象建立了联系,那么在二次标记时, A会被移出即将回收集合。之后,对象会再次出现没有引用的情况,在这个情况下finalize()不会被再次调用,对象会直接变为不可触及对象。也就是说finalize()只会被调用一次。
- 下面代码测试了 finalize机制的调用,复活对象,但finalize只能调用一次
public class ExampleUnitTest {
public static ExampleUnitTest instance;
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("调用了finalize()方法...");
// 如果不将instance和外部连接起来,那么第一次GC就已经为null了
instance = this;
}
@Test
public void addition_isCorrect() {
instance = new ExampleUnitTest();
instance = null;
System.gc();
try {
// 延迟为了保证GC能进行
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (instance != null) {
System.out.println("instance 还活着...");
} else {
System.out.println("instance 死了...");
}
instance = null;
System.gc();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (instance != null) {
System.out.println("instance 还活着...");
} else {
System.out.println("instance 死了...");
}
}
}
打印结果如下:
调用了finalize()方法...
instance 还活着...
instance 死了...