1.前言
前几天面试过程中被问到了垃圾收集,面试官问了我垃圾收集是怎么实现的,然后我就把运行时数据区里的堆里的新生代和老年代介绍了一遍,又指出新生代里的eden和survivor,再说了说复制算法,标记清除和标记整理算法。最后面试官又问我,那你说说怎么判断一个对象可以被回收把,我说了引用为0以及标记对象两次(也就是引用计数器和可达性算法)。重点来了,面试官又问我你说的两次标记,是什么意思呢?我哑口了。。我只记得有两次标记,但是并不知道是怎么实现的,于是面试完后记录下问题,开始准备这篇文章。
在弄明白标记对象两次前, 首先认识一下finalize()。
2.finalize() 方法
finalize位于object内,当垃圾回收器将要回收对象之前会检查当前对象的finalize()是否被重写以及是否被执行(因为finalize()只能执行一次),如果是被重写以及没被调用过就调用这个方法。但GC不保证方法会被执行完。也就是说两次标记是为了看死期将至的对象还有没有遗言要说。
3.两次标记
第一次标记:通过GC roots遍历,找到不在关系网内的对象。并检查是否需要执行finalize()方法。(也就是说如果没重写finalize()则只需要标记一次,然后就可以GC)
在第一次标记中有遗言(finalize()需要被执行)的对象,会被丢到一个优先级较低的队列中执行,但不保证执行完
第二次标记:对队列中的对象再遍历一次,还是不在关系网内的对象就被打上标记,待会送去人道毁灭。
看到这里恐怕你也会和我有一样的疑问:
第一,既然finalize()是遗言,为什么不让他说完再让他死呢?
第二,假如看到文章后,你和我一样也打开了eclipse调用起了object的finalize()方法,你会发现finalize()在JDK9已经被打上强烈不赞同(deprecation)的标记了。那么是不是从JDK9开始就变成GC只要标记一次了呢?
第一个问题是finalize运行可能会导致死锁或是死循环。
第二个问题说实话不太懂…那就上代码把
public static void main(String[] args){
QueueTest qt = new QueueTest();
qt=null;
System.gc();
}
@Override
protected void finalize()throws Exception {
System.out.println("救救我吧");
}
输出: 救救我吧
那就说明finalize()在GC机制中还在用。但是我还是不懂,既然不推荐用,为什么还要用呢?