java9 - 什么时候在Java中调用finalize()方法?




java9 finalize (11)

我需要知道何时在JVM调用finalize()方法。 我创建了一个测试类,通过覆盖它来调用finalize()方法时写入文件。 它没有执行。 任何人都可以告诉我它不执行的原因吗?


Java允许对象实现可能调用的名为finalize()的方法。

如果垃圾收集器试图收集对象,则会调用finalize()方法。

如果垃圾收集器没有运行,该方法不会被调用。

如果垃圾收集器未能收集对象并尝试 再次 运行 ,则该方法不会在第二次调用。

实际上,你不太可能在实际项目中使用它。

请记住,它可能不会被调用,它绝对不会被调用两次。 finalize()方法可以运行零次或一次。

在下面的代码中,当运行它时,finalize()方法不产生输出,因为程序在需要运行垃圾回收器之前退出。

Source


什么时候在Java中调用finalize()方法?

GC检测到对象不再可访问并且在它实际回收对象使用的内存之前,将调用finalize方法。

  • 如果一个对象永远无法访问, finalize()永远不会被调用。

  • 如果GC不运行,那么finalize()可能永远不会被调用。 (通常,GC只在JVM决定可能有足够的垃圾使其值钱时运行。)

  • 在GC确定某个特定对象无法访问之前,可能需要多个GC周期。 (Java GC通常是“世代”收集器......)

  • 一旦GC检测到对象无法访问并可终结,它就位于终结队列中。 终止通常与正常GC不同步发生。

(JVM规范实际上允许 JVM 永远不会运行终结器......只要它不回收对象使用的空间,以这种方式实现的JVM就会被瘫痪/无用,但它的行为是“允许的” 。)

结果是,依靠定稿来完成必须在确定的时间框架内完成的事情是不明智的。 这是“最佳实践”,根本不使用它们。 应该有一种更好的(即更可靠的)方法来做你在finalize()方法中要做的任何事情。

最终确定的唯一合法用途是清理与应用程序代码丢失的对象相关的资源。 即使这样,你也应该尝试编写应用程序代码,以便它不会丢失对象。 (例如,使用Java 7 + try-with-resources确保始终调用close() ...)

我创建了一个测试类,通过覆盖它来调用finalize()方法时写入文件。 它没有执行。 任何人都可以告诉我它不执行的原因吗?

这很难说,但有几种可能性:

  • 该对象不是垃圾收集,因为它仍然可以访问。
  • 该对象不是垃圾收集的,因为GC在测试完成之前不会运行。
  • 该对象由GC找到并由GC放入最终队列中,但在测试完成之前未完成最终化。

Java finalize()方法不是析构函数,不应该用于处理应用程序依赖的逻辑。 Java规范指出,不能保证在应用程序的生存期内调用finalize方法。

你可能想要的是finally和清理方法的结合,如下所示:

MyClass myObj;

try {
    myObj = new MyClass();

    // ...
} finally {

    if (null != myObj) {
        myObj.cleanup();
    }
}

finalize方法无法保证。当对象符合GC条件时,将调用此方法。 有很多情况下,物体可能不会被垃圾收集。


如果对象无法从任何活动线程或任何静态引用进行访问,换句话说,如果某个对象的所有引用都为空,则可以说对象有资格进行垃圾回收。 循环依赖不被视为引用,所以如果对象A引用了对象B,并且对象B引用了对象A,并且它们没有任何其他活动引用,则对象A和B都有资格进行垃圾收集。 通常情况下,对象在以下情况下有资格在Java中进行垃圾回收:

  1. 该对象的所有引用显式设置为null,例如object = null
  2. 对象在块内部创建,一旦控制器退出该块,引用就会超出范围。
  3. 父对象设置为空,如果一个对象拥有另一个对象的引用,并且当您设置容器对象的引用为null,则子对象或包含对象将自动变为符合垃圾回收的条件。
  4. 如果一个对象只有通过WeakHashMap的实时引用,它将有资格进行垃圾回收。

尝试运行此程序以获得更好的理解

public class FinalizeTest 
{       
    static {
        System.out.println(Runtime.getRuntime().freeMemory());
    }

    public void run() {
        System.out.println("run");
        System.out.println(Runtime.getRuntime().freeMemory());
    }

     protected void finalize() throws Throwable { 
         System.out.println("finalize");
         while(true)
             break;          
     }

     public static void main(String[] args) {
            for (int i = 0 ; i < 500000 ; i++ ) {
                    new FinalizeTest().run();
            }
     }
}

我们重写finalize方法的类

public class TestClass {    
    public TestClass() {
        System.out.println("constructor");
    }

    public void display() {
        System.out.println("display");
    }
    @Override
    public void finalize() {
        System.out.println("destructor");
    }
}

调用finalize方法的机会

public class TestGarbageCollection {
    public static void main(String[] args) {
        while (true) {
            TestClass s = new TestClass();
            s.display();
            System.gc();
        }
    }
}

当内存与转储对象重载时,gc将调用finalize方法

运行并查看控制台,在那里你不会经常调用finalize方法,当内存变得超载时,将调用finalize方法。


敲定将打印创建课程的计数。

protected void finalize() throws Throwable {
    System.out.println("Run F" );
    if ( checkedOut)
        System.out.println("Error: Checked out");
        System.out.println("Class Create Count: " + classCreate);
}

主要

while ( true) {
    Book novel=new Book(true);
    //System.out.println(novel.checkedOut);
    //Runtime.getRuntime().runFinalization();
    novel.checkIn();
    new Book(true);
    //System.runFinalization();
    System.gc();

如你看到的。 下面的输出显示当班级数量为36时,第一次执行gc。

C:\javaCode\firstClass>java TerminationCondition
Run F
Error: Checked out
Class Create Count: 36
Run F
Error: Checked out
Class Create Count: 48
Run F

有时当它被摧毁时,一个对象必须做出一个动作。 例如,如果某个对象具有非Java资源(如文件句柄或字体),则可以在销毁对象之前验证是否已释放这些资源。 为了管理这种情况,java提供了一种称为“finalizing”的机制。 通过最终确定它,您可以定义在即将从垃圾回收器中移除对象时发生的特定操作。 要为类添加终结器,只需定义finalize()方法。 Java执行时间在它要删除该类的对象时调用此方法。 在finalize 方法()中 ,指定在销毁对象之前执行的操作。 垃圾收集器会定期搜索不再引用任何运行状态的对象,也不会间接引用任何其他对象。 在资产被释放之前,Java运行时调用对象上的finalize()方法。 finalize()方法具有以下一般形式:

protected void finalize(){
    // This is where the finalization code is entered
}

使用受保护的关键字,可以防止其类中的代码对finalize()的访问。 理解finalize()在垃圾回收之前就被调用很重要。 例如,当对象离开作用域时它不会被调用。 这意味着你无法知道何时或如果finalize()会被执行。 因此,程序必须提供其他方法来释放系统资源或对象使用的其他资源。 您不应该依赖finalize()来正常运行程序。


检查Effective Java,第2版第27页。 项目7:避免终结器

终结器是不可预知的,通常是危险的,而且通常是不必要的。 从不在终结者中做任何时间关键的事情。 从不依赖终结器来更新关键持久状态。

要终止资源,请使用try-finally代替:

// try-finally block guarantees execution of termination method
Foo foo = new Foo(...);
try {
    // Do what must be done with foo
    ...
} finally {
    foo.terminate(); // Explicit termination method
}

通常最好不要依赖finalize()来进行清理等。

根据Javadoc (它值得阅读),它是:

当垃圾收集确定没有更多对该对象的引用时,由对象上的垃圾回收器调用。

正如Joachim所指出的那样,如果对象总是可以访问的,这可能永远不会发生在程序的生命中。

此外,垃圾收集器不保证在任何特定时间运行。 总的来说,我想说的是finalize()可能不是一般用到的最好的方法,除非有特定的东西需要它。







finalize