356

我需要知道何时finalize()JVM. finalize()我创建了一个测试类,该类在通过覆盖调用该方法时写入文件。它不被执行。谁能告诉我它没有执行的原因?

4

17 回答 17

390

finalize当对象即将被垃圾回收时调用该方法。这可以在它有资格进行垃圾收集之后的任何时间。

请注意,一个对象完全有可能永远不会被垃圾收集(因此finalize永远不会被调用)。这可能发生在对象永远不符合 gc 条件(因为它在 JVM 的整个生命周期内都可以访问)或者在对象变得符合条件和 JVM 停止运行之间实际上没有运行垃圾收集时(这通常发生在简单的测试程序)。

有一些方法可以告诉 JVMfinalize在尚未调用它的对象上运行,但使用它们也不是一个好主意(该方法的保证也不是很强)。

如果您依赖于finalize您的应用程序的正确操作,那么您做错了什么。finalize应该用于清理(通常是非 Java)资源。这正是因为 JVM 不保证finalize在任何对象上都会调用它。

于 2010-03-24T09:28:03.590 回答
292

一般来说,最好不要依赖finalize()做任何清理等。

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

当垃圾收集确定不再有对该对象的引用时,由对象上的垃圾收集器调用。

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

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

于 2010-03-24T09:30:41.927 回答
76
protected void finalize() throws Throwable {}
  • 每个类都finalize()从 java.lang.Object 继承方法
  • 当垃圾收集器确定不再存在对该对象的引用时,将调用该方法
  • Object finalize 方法不执行任何操作,但它可以被任何类覆盖
  • 通常它应该被覆盖以清理非Java资源,即关闭文件
  • 如果重写finalize(),使用 try-catch-finally 语句并始终调用super.finalize(). 这是一种安全措施,可确保您不会无意中错过关闭由对象调用类使用的资源

    protected void finalize() throws Throwable {
         try {
             close();        // close open files
         } finally {
             super.finalize();
         }
     }
    
  • 垃圾收集期间抛出的任何异常都会finalize()停止终结,否则会被忽略

  • finalize()永远不会在任何对象上运行超过一次

引自:http ://www.janeg.ca/scjp/gc/finalize.html

你也可以看看这篇文章:

于 2010-03-24T09:31:09.410 回答
26

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

您可能想要的是finally一种清理方法的组合,如:

MyClass myObj;

try {
    myObj = new MyClass();

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

这将正确处理MyClass()构造函数抛出异常时的情况。

于 2010-03-24T09:29:30.750 回答
20

查看 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
}
于 2013-07-01T14:20:56.740 回答
16

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

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

  • 如果一个对象永远不会变得不可访问,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 放入终结队列中,但在您的测试完成之前终结并未完成。
于 2010-03-24T09:28:50.073 回答
10

由于 JVM 调用 finalize() 方法存在不确定性(不确定是否会执行被覆盖的 finalize()),为了研究目的,观察调用 finalize() 时发生的情况的更好方法是强制 JVM 通过命令调用垃圾收集System.gc()

具体来说,finalize() 在对象不再使用时被调用。但是当我们试图通过创建新对象来调用它时,它的调用是不确定的。因此,为了确定,我们创建了一个显然没有未来用途的对象,因此我们看到了 objectnull的finalize 调用。cc

例子

class Car {

    int maxspeed;

    Car() {
        maxspeed = 70;
    }

    protected void finalize() {

    // Originally finalize method does nothing, but here we override finalize() saying it to print some stmt
    // Calling of finalize is uncertain. Difficult to observe so we force JVM to call it by System.gc(); GarbageCollection

        System.out.println("Called finalize method in class Car...");
    }
}

class Bike {

    int maxspeed;

    Bike() {
        maxspeed = 50;
    }

    protected void finalize() {
        System.out.println("Called finalize method in class Bike...");
    }
}

class Example {

    public static void main(String args[]) {
        Car c = new Car();
        c = null;    // if c weren`t null JVM wouldn't be certain it's cleared or not, null means has no future use or no longer in use hence clears it
        Bike b = new Bike();
        System.gc();    // should clear c, but not b
        for (b.maxspeed = 1; b.maxspeed <= 70; b.maxspeed++) {
            System.out.print("\t" + b.maxspeed);
            if (b.maxspeed > 50) {
                System.out.println("Over Speed. Pls slow down.");
            }
        }
    }
}

输出

    Called finalize method in class Car...
            1       2       3       4       5       6       7       8       9
    10      11      12      13      14      15      16      17      18      19
    20      21      22      23      24      25      26      27      28      29
    30      31      32      33      34      35      36      37      38      39
    40      41      42      43      44      45      46      47      48      49
    50      51Over Speed. Pls slow down.
            52Over Speed. Pls slow down.
            53Over Speed. Pls slow down.
            54Over Speed. Pls slow down.
            55Over Speed. Pls slow down.
            56Over Speed. Pls slow down.
            57Over Speed. Pls slow down.
            58Over Speed. Pls slow down. 
            59Over Speed. Pls slow down.
            60Over Speed. Pls slow down.
            61Over Speed. Pls slow down.
            62Over Speed. Pls slow down.
            63Over Speed. Pls slow down.
            64Over Speed. Pls slow down.
            65Over Speed. Pls slow down.
            66Over Speed. Pls slow down.
            67Over Speed. Pls slow down.
            68Over Speed. Pls slow down.
            69Over Speed. Pls slow down.
            70Over Speed. Pls slow down.

注意- 即使在打印到 70 之后并且在程序中没有使用对象 b 之后,也不确定 b 是否被 JVM 清除,因为没有打印“在类 Bike 中调用 finalize 方法...”。

于 2015-05-13T11:34:24.457 回答
5

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
于 2012-11-24T23:17:31.390 回答
4

最近与终结器方法搏斗(为了在测试期间处理连接池),我不得不说终结器缺少很多东西。使用 VisualVM 观察以及使用弱引用来跟踪实际交互我发现在 Java 8 环境(Oracle JDK、Ubuntu 15)中以下情况是正确的:

  • Finalize 不会立即被调用,Finalizer(GC 部分)单独拥有引用难以捉摸
  • 默认垃圾收集器池无法访问的对象
  • Finalize 被批量调用,指向一个实现细节,即垃圾收集器在某个阶段释放资源。
  • 调用 System.gc() 通常不会导致对象更频繁地被终结,它只会导致终结器更快地意识到无法访问的对象
  • 由于在执行堆转储或某些其他内部机制期间的高堆开销,创建线程转储几乎总是会导致触发终结器
  • 终结接缝受内存要求(释放更多内存)或标记为终结增长的某个内部限制的对象列表的约束。因此,如果您有很多对象要完成,那么与只有少数几个相比,完成阶段会更频繁、更早地触发
  • 在某些情况下,System.gc() 直接触发了 finalize,但前提是引用是本地的且短暂的。这可能与世代有关。

最后的想法

Finalize 方法不可靠,但只能用于一件事。您可以确保对象在被垃圾收集之前已关闭或处置,如果正确处理涉及生命周期结束操作的具有更复杂生命周期的对象,则可以实现故障安全。这就是我能想到的让它值得覆盖它的一个原因。

于 2015-09-25T11:08:13.987 回答
2

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

于 2010-03-24T10:48:13.253 回答
2

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

  1. 该对象的所有引用都显式设置为 null,例如 object = null
  2. 对象是在块内创建的,一旦控制退出该块,引用就会超出范围。
  3. 父对象设置为 null,如果一个对象持有另一个对象的引用,并且当您将容器对象的引用设置为 null 时,子对象或包含的对象自动成为垃圾回收的条件。
  4. 如果一个对象只有通过 WeakHashMap 的实时引用,它将有资格进行垃圾收集。
于 2013-04-09T12:01:59.407 回答
2

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

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

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

于 2017-07-02T20:19:10.273 回答
1

我们覆盖 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 方法。

于 2016-02-01T11:37:05.910 回答
0

正如https://wiki.sei.cmu.edu/confluence/display/java/MET12-J.+Do+not+use+finalizers中所指出的,

由于执行时间取决于 Java 虚拟机 (JVM),因此没有必须执行终结器的固定时间。唯一的保证是,任何执行的终结器方法都会在关联对象变得无法访问(在垃圾回收的第一个周期期间检测到)之后和垃圾回收器回收关联对象的存储之前(在垃圾回收器的第二个周期期间)执行此操作. 在对象变得无法访问后,对象的终结器的执行可能会延迟任意长的时间。因此,在对象的 finalize() 方法中调用时间关键功能(例如关闭文件句柄)是有问题的。

于 2019-11-30T11:46:28.260 回答
0

Java 允许对象实现一个名为 finalize() 的方法,该方法可能会被调用。

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

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

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

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

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

在下面的代码中,finalize() 方法在我们运行它时不会产生任何输出,因为程序在需要运行垃圾收集器之前就退出了。

资源

于 2016-07-25T09:07:50.960 回答
0

finalize()在垃圾收集之前调用。当对象超出范围时不会调用它。这意味着您无法知道何时甚至是否finalize()会被执行。

例子:

如果您的程序在垃圾收集器发生之前结束,则finalize()不会执行。因此,它应该用作备份程序以确保正确处理其他资源,或用于特殊用途的应用程序,而不是作为您的程序在其正常运行中使用的手段。

于 2018-04-17T07:09:57.637 回答
-3

尝试运行此程序以更好地理解

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();
            }
     }
}
于 2017-01-17T13:21:30.653 回答