解析
一. finalize的作用
- finalize()是Object的protected方法,子类可以覆盖该方法以实现资源清理工作,GC在回收对象之前调用该方法。
- finalize()与C++中的析构函数不是对应的。C++中的析构函数调用的时机是确定的(对象离开作用域或delete掉),但Java中的finalize的调用具有不确定性
- 不建议用finalize方法完成“非内存资源”的清理工作,但建议用于:
- ① 清理本地对象(通过JNI创建的对象);
- ② 作为确保某些非内存资源(如Socket、文件等)释放的一个补充:在finalize方法中显式调用其他资源释放方法。
二. finalize的生命周期
当对象变成不可达时, GC会判断该对象是否已经覆盖过finalize方法, 如果没有覆盖, 则直接回收, 否则, 若对象未执行过finalize方法, 将其放入F-Queue队列, 由一低优先级线程执行该队列对象中的finalize方法. 执行完毕后, GC会再次判断可达性(即只有一次自救的机会), 若不可达, 则直接进行回收, 否则对象**“复活”**.
图解
生命周期解释
- 新建对象首先处于[reachable, unfinalized] 状态 (A)
- 随着程序运行, 一些引用关系会消失, 从reachable状态迁移到f-unreachable 或 unreachable状态.
- 若JVM检测到处于unfinalized状态的对象变成f-reachable或unreachable, JVM会标记为fianlizable状态(G H), 也就是可以进行finalize方法的状态
- 在某个时刻, JVM取出某个finalizable对象, 标记为finalized并在某个线程中执行该对象的finalize方法.
- 处于finalizable状态的对象不能同时为unreachable, 因为finalizable对象标记为finalized时, 某个线程会执行该对象的finalize方法, 使其变成reachable.
- 手动调用finalize方法对于状态转换是无效的(不影响JVM的行为).
- 若JVM检测到finalized状态的对象变成不可达(unreachable), 就回收其内存(I)
- 若对象并未覆盖finalize方法, JVM会对其优化, 直接回收对象(O)
实战代码
/**
* 演示内容:
* 1. 对象可以在被GC时自我拯救
* 2. 这种自救的机会只有一次, 因为一个对象的finalize()方法最多被调用一次
*/
public class FinalizeEscapeGC {
public static FinalizeEscapeGC SAVE_HOOK = null;
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("触发finalize方法...");
// 进行拯救
FinalizeEscapeGC.SAVE_HOOK = this;
}
public void isAlive() {
System.out.println("我复活了 ^ ^");
}
public static void main(String[] args) throws InterruptedException{
SAVE_HOOK = new FinalizeEscapeGC();
// 对象被GC回收前执行finalize方法, 可以有一次自我拯救的机会
SAVE_HOOK = null;
System.gc();
// finalize方法优先级低(JVM会调用一个优先级低的线程执行Queue-F队列中的finalize方法)
// 保证finalize方法已经执行完毕
Thread.sleep(1000);
if (null != SAVE_HOOK) {
SAVE_HOOK.isAlive();
} else {
System.out.println("我死了 TAT");
}
// 尝试再次自救
SAVE_HOOK = null;
System.gc();
// 因为finalize()方法优先级很低, 保证执行
Thread.sleep(1000);
if (null != SAVE_HOOK) {
SAVE_HOOK.isAlive();
} else {
System.out.println("我死了 TAT");
}
}
}
实战结果
触发finalize方法...
我复活了 ^ ^
我死了 TAT
说明对象最多只能执行一次自救, 之后不会再调用finalize方法.