文章目录

finalization原理

java提供的对象终止(finalization)机制主要用来处理对象被回收前的处理逻辑,主要是通过调用对象的finalize()方法来处理对象终止逻辑。finalize()方法允许被重写,一般在该方法中进行一些资源释放和清理的工作,比如关闭文件、套接字和数据库连接等。

不要主动调用finalize()方法,交给垃圾回收器回收垃圾时进行调用。原因如下:

  • finalize()方法会导致对象复活;
  • fizalize()执行时间是无法保障的,它完全由GC线程决定,极端情况下,若不发生GC,将不会执行。finalize方法

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

  • 可触及的:从根节点开始,可以达到这个对象;
  • 可复活的:对象的所有引用都被释放,但是对象有可能在finalize()中复活;
  • 不可触及的:对象的finalize()方法被调用,并且没有复活,那么就会进入不可触及状态,不可触及的对象不可能被复活,因为finalize()只会被调用一次。
    一般来说,从根节点开始无法触及的对象需要会垃圾回收器回收掉的,但是该对象并不是一定要被回收的,一个无法触及的对象可能在满足指定条件下可以复活对象本身。

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

  1. 加入GC Root到对象A没有了引用了关系,则进行第一次标记;
  2. 第一轮标记结束后,进行第二轮,判断对象是否有必要执行finalize()方法。如果对象A没有重写finalize()方法,或者该方法已经被虚拟机调用过,则对象A的finalize()方法就不会被执行,并且对象A被判定为不可触及的;如果对象A重写了finalize()方法,并且还未执行过,那么对象A就会被加入到F-Queue队列中,由一个虚拟机自动创建的、低优先级的Finalizer线程触发队列中每个对象的finalize()方法。
    finalize()方法是即将被回收对象逃脱回收的最后机会,稍后GC会对G-Queue队列中的对象进行第二次标记。如果对象A在finalize()方法中与引用链上的任何对象建立了联系,那么第二次标记时,对象A会被移出即将回收的集合(F-Queue),如果之后对象A又出现了不存在引用的情况,没有对象指向它,此时finalze()方法不会再被执行,对象A会直接变成不可触及的状态,然后等待被回收,因此finalize()方法只会被执行一次。

案例一

public class FinalizeDemo {
    private static FinalizeDemo finalizeObject;

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        finalizeObject = this;
    }

    public static void main(String[] args) throws InterruptedException {
        finalizeObject = new FinalizeDemo();
        finalizeObject = null;
        System.gc();
        System.out.println("第一次发生gc...");
        //睡眠的目的是因为Finalizer的线程优先级低,睡眠一段时间便于Finalizer获取执行权限
        Thread.sleep(1000);
        if (finalizeObject == null){
            System.out.println("finalizeObject is killed");
        }else {
            System.out.println("finalizeObject is alive");
        }

        System.out.println("第二次发生gc...");
        finalizeObject = null;
        System.gc();
        Thread.sleep(2000);
        if (finalizeObject == null){
            System.out.println("finalizeObject is killed");
        }else {
            System.out.println("finalizeObject is alive");
        }
    }
}

执行结果如下:

第一次发生gc...
finalizeObject is alive
第二次发生gc...
finalizeObject is killed

首先finalizeObject开始是指向的FinalizeDemo类型的一个对象引用,finalizeObject存在栈上,new FinalizeDemo()存在堆上,由于finalizeObject是一个静态的成员变量,会被加入GC Root集合,finalizeObject=null后,按理说堆上的FinalizeDemo对象应该是要被回收的,但是在回收之前会先执行finalize()方法,由于在finalize()方法中,变量finalizeObject又指向了this对象,导致finalizeObject又有了新的引用链finalizeObject=this。当第二次领finalizeObject=null后,由于finalizeObject的行为与第一次相同,都是指向了当前对象,此时虚拟机不会执行第二次finalize()方法,导致finalizeObject引用链中断,指向了null。

案例二
代码与上述案例一一致,只是把finalize()方法改为如下所示

@Override
protected void finalize() throws Throwable {
    super.finalize();
    finalizeObject = new FinalizeDemo();
}

然后重新执行案例输出如下:

第一次发生gc...
finalizeObject is alive
第二次发生gc...
finalizeObject is alive

为什么finalizeObject第二次被救活了呢?因为finalizeObject = new FinalizeDemo()执行了两次,两次行为不一样,每一次finalizeObject都执行了堆中一个FinalizeDemo类型对象,导致Finalizer线程第二次同样执行了重写的finalize()方法。而案例一种finalizeObject两次都引用了相同的this对象,导致Finalizer线程第二次没有执行finalize()方法。


版权声明:本文为u010502101原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/u010502101/article/details/119855913