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

Raj*_*r J 320 java methods garbage-collection finalize call

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

Joa*_*uer 369

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

请注意,对象永远不会被垃圾收集(因此finalize永远不会被调用).当对象从来就符合GC(因为它是可到达通过JVM的整个生命周期),或在没有垃圾收集的时间之间实际运行对象资格和JVM停止运行的时间(这往往与简单的时就会发生这种情况测试程序).

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

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

  • @VikasVerma:完美的替代品是没有的:你不应该需要它们.唯一有意义的情况是你的类管理一些外部资源(比如TCP/IP连接,文件...... Java GC无法处理的任何东西).在这些情况下,`Closable`接口(以及它背后的想法)可能就是你想要的:make` .close()`关闭/丢弃资源并要求你的类用户在适当的时候调用它.你*可能*想要添加一个`finalize`方法"只是为了保存",但这更像是一个调试工具,而不是一个实际的修复(因为它不够可靠). (5认同)
  • @Dragonborn:这实际上是一个完全不同的问题,应该单独询问.有关闭挂钩,但如果JVM意外关闭(又称崩溃),则无法保证.但是他们的保证明显强于终结者(并且它们也更安全). (4认同)
  • 您的最后一段说明仅用于清理资源,即使无法保证它将被调用.这种乐观主义在说什么?我认为某些东西不可靠,因为这也不适合清理资源. (4认同)
  • @Rajesh.不,这不是"寿命"问题.您可以将程序置于无限循环中(多年),如果不需要垃圾收集器,它将永远不会运行. (2认同)
  • 终结器有时可以节省一天的时间...我遇到了一个第三方库正在使用FileInputStream却从未关闭的情况。我的代码正在调用该库的代码,然后尝试移动该文件,但由于该文件仍处于打开状态而失败。我不得不强制调用`System.gc()`来调用FileInputStream :: finalize(),然后才能移动文件。 (2认同)

小智 268

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

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

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

正如Joachim指出的那样,如果对象始终可访问,这可能永远不会发生在程序的生命周期中.

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

  • "不是最好的使用方法......除非有特定的东西你需要它" - 呃,这句话适用于100%的一切,所以没有用.Joachim Sauer的答案要好得多 (107认同)
  • 换句话说(仅为了澄清未来的读者)它从不在主类上调用,因为当主类关闭时,不需要收集垃圾.操作系统会清理应用程序使用的所有内容. (18认同)
  • 那么`finalize`会有用的情况是什么? (3认同)
  • @MarkJeronimus - 实际上,这是无关紧要的.当类的>> instance <<被垃圾收集时,而不是当main方法终止时,调用主类的`finalize()`方法.此外,主要类*可以在应用程序完成之前进行垃圾收集; 例如,在一个多线程应用程序中,"主"线程创建其他线程然后返回.(实际上,需要一个非标准的类加载器....) (3认同)

Xpi*_*itO 71

protected void finalize() throws Throwable {}
Run Code Online (Sandbox Code Playgroud)
  • 每个类都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();
         }
     }
    
    Run Code Online (Sandbox Code Playgroud)
  • finalize()垃圾收集期间抛出的任何异常都会停止终结,但会被忽略

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

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

您还可以查看这篇文章:

  • 您链接到的JavaWorld文章是从1998年开始的,并且有一些有趣的建议,特别是建议在调用JVM退出之前调用System.runFinalizersOnExit()以确保终结器运行.该方法目前已被弃用,注释'此方法本质上是不安全的.它可能导致在活动对象上调用终结器,而其他线程同时操作这些对象,导致不稳定的行为或死锁.所以我不会这样做. (25认同)
  • 由于runFinalizerOnExit()不是线程安全的,因此可以做的是Runtime.getRuntime().addShutdownHook(new Thread(){public void run(){destroyMyEnclosingClass();}}); 在类的构造函数中. (3认同)
  • @Ustaman Sangat这是一种方法,但请记住这会从shutdownHook设置对您的实例的引用,这几乎可以保证您的类永远不会被垃圾回收.换句话说,这是一个内存泄漏. (2认同)

rsp*_*rsp 25

Java finalize()方法不是析构函数,不应该用于处理应用程序所依赖的逻辑.Java规范声明无法保证finalize在应用程序的实时时间内完全调用该方法.

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

MyClass myObj;

try {
    myObj = new MyClass();

    // ...
} finally {

    if (null != myObj) {
        myObj.cleanup();
    }
}
Run Code Online (Sandbox Code Playgroud)


Hao*_*eng 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
}
Run Code Online (Sandbox Code Playgroud)

  • 或者使用try-with-resources (3认同)
  • 假设对象的生存期在一个函数的范围内。当然,这不是OP所指的情况,基本上也不是任何人都需要的情况。考虑“缓存引擎返回值参考计数”。您要在释放最后一个引用时释放缓存条目,但是您不知道何时释放最后一个引用。例如,finalize()可以减少引用计数。但是,如果您要求用户显式调用一个自由函数,则要求内存泄漏。通常我只是同时做这两个事情(释放功能+双重检查以完成...)... (3认同)

Ste*_*n C 16

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

在GC检测到对象不再可访问之后,以及在实际回收对象使用的内存之前,将调用finalize方法.

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

  • 如果GC未运行,则finalize()可能永远不会被调用.(通常情况下,只有当JVM确定可能存在足够的垃圾才能使其值得时,GC才会运行.)

  • 在GC确定特定对象无法访问之前,可能需要多个GC周期.(Java GCs通常是"世代"收藏家...)

  • 一旦GC检测到一个对象无法访问并可终结,它就会被置于一个终结队列中.终结通常与普通GC异步发生.

(JVM规范,实际上是允许一个JVM来从未运行终结......只要它不回收对象使用的空间.这是这种方式实现一个JVM将被削弱/没用,但这种行为是"允许" .)

结果是依靠最终化来完成必须在确定的时间范围内完成的事情是不明智的.根本不使用它们是"最佳实践".应该有一种更好(即更可靠)的方式来做你在该finalize()方法中尝试做的任何事情.

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


我创建了一个测试类,当通过覆盖它调用finalize()方法时,该测试类会写入文件.它没有被执行.谁能告诉我它没有执行的原因?

很难说,但有一些可能性:

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


tec*_*109 10

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

具体来说,当对象不再使用时调用finalize().但是当我们试图通过创建新对象来调用它时,它的调用并不确定.因此,对于把握我们创建了一个null对象c,这显然是没有前途的使用,因此我们看到物体c的敲定电话.

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.");
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

产量

    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.
Run Code Online (Sandbox Code Playgroud)

注意 - 即使在打印到70之后并且在程序中没有使用对象b之后,仍然存在由JVM清除b的不确定性,因为不打印"在Bike类中调用最终方法...".

  • 调用`System.gc();`并不能保证实际运行垃圾收集. (10认同)
  • 它也不能保证将运行*kind*的集合.因为大多数Java GC都是"世代"收藏家. (3认同)

小智 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);
}
Run Code Online (Sandbox Code Playgroud)

主要

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

如你看到的.以下输出显示gc在班级计数为36时第一次执行.

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
Run Code Online (Sandbox Code Playgroud)