目录

  1. 对象的 finalization 机制
  2. 对象的状态
  3. 对象的两次标记过程

如果从所有的根节点都无法访问到某个对象,说明对象己经不再使用了。一般来说,此对象需要被回收。但事实上,不可达对象也并非是非死不可的,这时候的它们暂时处于缓刑阶段,一个无法触及的对象有可能在某一条件下复活自己,如此再对此对象的回收就是不合理的,本文就主要讲解在什么情况下,对象会复活

对象的 finalization 机制

Java 语言提供了对象终止(finalization)机制允许开发人员提供对象被销毁之前的自定义处理逻辑,即:垃圾回收器回收此对象之前,总会先调用这个对象的finalize()方法

finalize()方法特点

finalize() 方法允许在子类中被重写,用于在对象被回收时进行资源释放。通常在这个方法中进行一些资源释放和清理的工作,比如关闭文件、套接字和数据库连接等。此方法只会被调用一次

🎶永远不要手动调用某个对象的finalize()方法,应该交给垃圾回收机制调用,包括下面三点原因:

  1. finalize()时可能会导致对象复活

  2. finalize()方法的执行时间是没有保障的,它完全由 GC 线程决定,极端情况下,若不发生 GC,则finalize()方法将没有执行机会

  3. 一个糟糕的finalize()会严重影响 GC 的性能

从功能上来说,finalize()方法与 C++中的析构函数比较相似,但是 Java 采用的是基于垃圾回收器的自动内存管理机制,所以finalize()方法在本质上不同于 C++中的析构函数

对象的状态

✨由于finalize()方法的存在,虚拟机中的对象一般处于三种可能的状态:可触及的可复活的不可触及的

  • 可触及的:从根节点开始,可以到达这个对象
  • 可复活的:对象的所有引用都被释放,但是对象有可能在finalize()中复活。
  • 不可触及的:对象的finalize()被调用,并且没有复活,那么就会进入不可触及状态。不可触及的对象不可能被复活(finalize()只会被调用一次)并且一旦对象进入不可触及状态,此对象一定会被回收

对象的两次标记过程

判定一个对象 objA 是否可回收,至少要经历两次标记过程

  1. 如果对象 objA 到 GC Roots 没有引用链,则进行第一次标记
  2. 进行筛选,判断此对象是否有必要执行finalize()方法
  3. 如果对象 objA 没有重写finalize()方法,或者finalize()方法已经被虚拟机调用过,则虚拟机视为“没有必要执行”,objA 被判定为不可触及的状态
  4. 如果对象 objA 重写了finalize()方法,且还未执行过,那么 objA 会被插入到 F-Queue 队列中,由虚拟机自动创建的、低优先级的 Finalizer 线程触发其finalize()方法
  5. finalize()方法是对象逃脱死亡的最后机会,稍后 GC 会对 F-Queue 队列中的对象进行第二次标记

如果 objA 在finalize()方法中与引用链上的任何一个对象建立了联系,那么在第二次标记时,objA 会被移出即将回收集合。随后,对象会再次出现没有引用存在的情况。在这个情况下,finalize 方法不会被再次调用(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
public class CanReliveObj {
// 类变量,属于GC Roots的一部分
public static CanReliveObj canReliveObj;

@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("调用当前类重写的finalize()方法");
canReliveObj = this;
}

public static void main(String[] args) throws InterruptedException {
canReliveObj = new CanReliveObj();
canReliveObj = null;
System.gc();
System.out.println("-----------------第一次gc操作------------");
// 因为Finalizer线程的优先级比较低,暂停2秒,以等待它
Thread.sleep(2000);
if (canReliveObj == null) {
System.out.println("obj is dead");
} else {
System.out.println("obj is still alive");
}

System.out.println("-----------------第二次gc操作------------");
canReliveObj = null;
System.gc();
// 下面代码和上面代码是一样的,但是 canReliveObj却自救失败了
Thread.sleep(2000);
if (canReliveObj == null) {
System.out.println("obj is dead");
} else {
System.out.println("obj is still alive");
}

}
}
/**
-----------------第一次gc操作------------
调用当前类重写的finalize()方法
obj is still alive
-----------------第二次gc操作------------
obj is dead
**/