什么是对象终止机制?
Java语言提供了对象终止( finalization )机制来允许开发人员提供对象被销毁之前的自定义处理逻辑处理。
当垃圾回收器发现没有任何引用指向某个对象时,那么就会在垃圾回收中清除这个对象,在垃圾回收器回收此对象之前,会先调用这个对象的 finalize() 方法。
我们发现 finalize() 方法允许在子类中被重写,所以我们可以利用这个重写的方法,用于在对象被回收时进行资源的释放操作或者一些清理操作,比如:关闭文件、套接字和数据库连接等。
从功能上来说,finalize() 方法与 C++ 中的析构函数比较相似,但是Java采用的是基于垃圾回收器的自动内存管理机制,它们在本质上还有区别。
对象终止的注意细节
finalize() 方法在一个对象生命周期中,只会被调用一次( 即使被复活过 )。我们开发人员切记不要主动调用某个对象的 finalize() 方法,而是应该交给垃圾回收机制去调用。因为在 finalize() 方法中,我们可以导致对象重新复活,也就是不让垃圾回收器回收,这会影响垃圾回收器本身的工作。因为 finalize() 方法的执行时间是没有保障的,它完全由GC线程决定,也就是说,若不发生GC,那么 finalize() 方法将永远没有机会执行。因为一个糟糕的 finalize() 方法会严重影响GC的性能,所以也不建议自己去调用。
对象的三种生命状态
由于finalize()方法的存在,虚拟机中的对象一般会有三种可能的状态。
如果从所有的根节点都无法访问到某个对象,说明对象己经不再使用了。一般来说,此对象需要被回收。但事实上,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段。因为一个无法触及的对象有可能在某一个条件下“复活”自己,如果这样,那么对它的回收就是不合理的。
- 可触及的状态:从GC Roots根节点开始,可以到达这个对象
- 可复活的状态:对象的所有引用都已经被释放,但是对象有可能在 finalize() 方法中复活
- 不可触及的状态:对象的 finalize() 被调用后发现也没有被复活,那么就会进入不可触及状态,不可触及的对象不可能再被复活,因为 finalize() 只会被调用一次
对象的回收判定过程
判定一个对象A是否可回收,至少要经历两次标记过程。
如果对象A到GC Roots没有引用链,则进行第一次标记:
- 如果对象A没有重写 finalize() 方法,或者 finalize() 方法已经被虚拟机调用过了,则虚拟机视为“没有必要执行”,此时对象A被判定为不可触及的状态
- 如果对象A重写了 finalize() 方法,且还未执行过,那么对象A会被插入到F-Queue的队列中,然后由虚拟机自动创建的、低优先级的Finalizer线程触发其 finalize() 方法执行
对象A会被插入到F-Queue的队列中后, GC会对这个队列中的所有对象进行第二次标记:
- 如果对象A在 finalize() 方法中也没有与引用链上的任何一个对象建立联系,那么在第二次标记时,对象A就会被判定为不可触及的状态
- 如果对象A在 finalize() 方法中与引用链上的任何一个对象建立了联系,那么在第二次标记时,对象A就会被判定为可复活的状态。 但是当对象再次出现没有引用存在的情况时, finalize() 方法也不会被再次调用,直接被判定为不可触及的状态。
对象的回收过程演示
public class CanReliveObj {
public static CanReliveObj obj;
// 重写finalize方法(第二次标记)
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("调用当前类重写的finalize()方法");
obj = this;
}
public static void main(String[] args) {
try {
obj = new CanReliveObj();
obj = null;
System.gc(); // 第一次标记
System.out.println("第1次 gc");
Thread.sleep(2000);
if (obj == null)
System.out.println("obj is dead");
else
System.out.println("obj is still alive");
obj = null;
System.gc();
System.out.println("第2次 gc");
Thread.sleep(2000);
if (obj == null)
System.out.println("obj is dead");
else
System.out.println("obj is still alive");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
当把上面重写的finalize方法取消时:
当把上面重写的finalize方法加上时:
什么是GC Roots?
上面提到的GC Roots是什么,它是垃圾回收器在垃圾回收时进行判断的根搜索对象集合,那么如何利用工具查看代码中的GC Roots呢?
编写测试代码
public class GCRootsTest {
public static void main(String[] args) {
List<Object> numList = new ArrayList<>();
Date birth = new Date();
for (int i = 0; i < 100; i++) {
numList.add(String.valueOf(i));
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("数据添加完毕...");
new Scanner(System.in).next();
numList = null;
birth = null;
System.out.println("对象已被置空...");
}
}
获取堆内存的Dump文件
命令行方式
# 查看当前进程
jps
# 生成dump文件
jmap -dump:format=b,live,file=test1.bin 进程ID
参考: |
标签:Java,finalize,对象,虚拟机,System,回收,终止,obj,方法 From: https://www.cnblogs.com/xfeiyun/p/17351522.html