finalize()方法的执行时机是什么?

  • Post category:Java

finalize()方法是Java中的一个方法,它可以被子类重写以完成一些资源回收等操作。但它的使用是有一定限制的。

finalize()方法的执行时机

finalize()方法的执行时机不是由程序员显式调用的,而是由垃圾收集器在回收对象之前判定该对象是否自救失效而自动调用的。 具体来说,虚拟机会在以下场景下调用finalize()方法:

  1. 对象被垃圾回收器回收前,JVM会调用该对象的finalize()方法,用于完成一些资源回收等操作。
  2. 对象的finalize()方法只会被调用一次。如果该对象在finalize()方法中没有被释放,那么对象的内存仍然会得不到释放,导致内存泄漏。

finalize()方法的使用限制和注意事项

finalize()方法的使用是有一定限制和注意事项的:

  1. 在Java9中,finalize()方法已被声明为过时方法,强烈建议不要再使用此方法。
  2. finalize()方法在使用时需要特别小心,因为它的对象的成员变量和对象自身可能被GC回收, 可能引起一些意想不到的后果。比如,在finalize()方法中针对成员变量的操作实际上已经发生在垃圾回收之前。
  3. finalize()方法的执行时机并不可预测,可能会给程序执行带来不可预测的结果。

示例

下面用两个简单的例子来说明finalize()方法的使用。

示例1:使用finalize()方法释放系统资源

public class FinalizeDemo {
    private File file;

    public FinalizeDemo(File file) {
        this.file = file;
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("资源释放: " + file.getName());
        file.delete();
        super.finalize();
    }

    public static void main(String[] args) {
        FinalizeDemo fd = new FinalizeDemo(new File("test.txt"));
        try (PrintWriter pw=new PrintWriter(fd.file)){
            pw.println("hello world");
        } catch (IOException e) {
            e.printStackTrace();
        }
        fd = null;
        System.gc();
    }
}

在上面例子中,finalize()方法被用来释放打开的文件资源,当系统调用垃圾回收机制时就可以调用finalize()方法关闭文件。

    @Override
    protected void finalize() throws Throwable {
        System.out.println("资源释放: " + file.getName());
        file.delete();
        super.finalize();
    }

finally()方法中,File对象被删除了。这样就确保了不会有残留的文件,因为它在该对象被垃圾回收机制回收时被调用。

FinalizeDemo fd = new FinalizeDemo(new File("test.txt"));
try (PrintWriter pw=new PrintWriter(fd.file)){
    pw.println("hello world");
} catch (IOException e) {
    e.printStackTrace();
}
fd = null;
System.gc();//通知JVM回收垃圾

示例2:对象自我拯救

public class FinalizeDemo {
    public static FinalizeDemo instance = null;

    @Override
    protected void finalize() throws Throwable {
        System.out.println("finalize()方法被调用");
        instance = this;
    }

    public static void main(String[] args) throws InterruptedException {
        instance = new FinalizeDemo();
        instance = null;

        //下面的代码和程序运行的时间长短有关
        System.gc();
        Thread.sleep(1000);
        if (instance != null) {
            System.out.println("对象正在被使用");
        } else {
            System.out.println("对象已经被回收");
        }
    }
}

在上面例子中,instance成员变量指向当前对象,在finalize()方法中,将instance变量赋值为当前对象。当程序运行之后,对象处于“可恢复状态”,即finalize()方法被调用后可以将对象的引用赋给instance变量,对象就恢复了。接下来调用System.gc()后等待1秒钟,JVM将回收该对象并调用finalize()方法,将对象的引用赋给instance变量,恢复该对象。最后判断instance变量是否为空以确定对象是否存活。

总之,finalize()方法的使用并不外推。在Java 9与Java 10之后的版本,finalize()方法已被声明为过时方法,JVM也不再保证它会被调用。使用时务必小心,避免给程序执行带来不可预测的后果。