为啥不调用 finalize()? [复制]

Posted

技术标签:

【中文标题】为啥不调用 finalize()? [复制]【英文标题】:Why finalize() is not called? [duplicate]为什么不调用 finalize()? [复制] 【发布时间】:2013-11-21 01:44:01 【问题描述】:

我想用 finalize 检查终止条件,但每次都不会执行 finalize。谁能告诉我为什么?

public class Test


    public static void main(String[] args)
       
        Tank tank=new Tank();
        tank.fill();
        System.gc();
     
  
public class Tank    

    private boolean emptied=true;

    public void fill()
    
        this.emptied=false;
    

    public void empty()
    
        this.emptied=true;
    

    public Tank()
    
        this.emptied=true;
    

    protected void finalize()
    
        if(this.emptied==false)
        
            System.out.println("Termination Verification Error: Tank should be emptied");
        
    

【问题讨论】:

因为你仍然有一个对象的引用,那你为什么期望它被gc'd? 所以如果他们设置tank=null 它应该可以工作吗?老实说,我从未在 Java 中使用过解构。 一般情况下,JVM不保证在对象销毁后直接执行finalizer。因此,如果您有一些必须执行的终止代码,请使用自定义终止方法 e。 G。通过实现Closeable / AutoCloseable 接口。请参阅 Effective Java,第 7 项:避免使用终结器 @JasonSperske 理论上。在调用System.gc() 之前设置tank=null(或将其设置为其他内容;重点是没有对感兴趣对象的剩余引用)将鼓励它起作用。您仍然不能保证 GC 在您调用 gc() 时会实际运行,或者它会实际丢弃该对象,或者它会在该对象上调用 finalize() - 但是如果您 设置tank=null 你绝对可以保证这一切都不会发生。 @Robin Krahl ---说的很清楚很直接,谢谢,以后会用这个。 【参考方案1】:

finalize() 方法仅在对象被 GC 销毁和终结时调用,这发生在对对象的最后一个引用被丢弃后的某个未定义的时间点。如果该对象从未被释放(例如,您仍然有对它的引用),则永远不会调用 finalize()

还请记住,您无法控制何时 finalize() 被调用,或者它是否会被调用。 See general description of Object.finalize().

即使System.gc() 也不能保证强制GC 做任何事情。这只是对 JVM 的一个建议。 See general description of System.gc()

一般来说,如果您发现自己试图像这样操作 GC 以执行这些类型的检查,通常会有更好的方法。这是对 finalize() 和 GC 的不当使用。

例如,如果您要检查假定的条件是否为真,请考虑在适当的位置使用assert(但请注意:assert 不能替代功能性if;它只是自行记录和测试设计时假设的条件)。

如果您的应用程序的高级功能的一部分是在退出之前验证条件是否为真,那么您应该在退出之前明确验证条件是否为真,例如在你的情况下:

public static void main(String[] args)
   
    Tank tank=new Tank();
    tank.fill();
    // check explicitly before terminating
    if (!tank.empty()) 
        System.err.println("Warning: Ending with non-empty tank!");
 

除了可以尝试从错误中恢复之外,显式检查还可以让您完全控制它何时发生。

更新:MadProgrammer 在下面的 cmets 中帮助提出的另一个不错的选择是使用 shutdown hook,如果这适合您的话。如果您的应用程序有多个您无法消除或控制的退出点,这将提供一种在关闭时退出代码的干净方式。

对于更复杂的应用程序,顺便说一下,你会发现单元测试等测试策略,结合良好的模块化设计(例如明确定义的前置条件、后置条件、不变量),可以提供完整的应用程序逻辑测试,而无需要求自检成为应用程序正常运行的一部分。并不是说后者有什么问题,尤其是在简单的应用程序中,但您可能有兴趣了解它以供将来参考。

【讨论】:

谢谢杰森。明白了,所以即使显式写finalize(),gc也不会每次都调用它。我发现每次调用 finalize 都有解决方案 System.runFinalizersOnExit(true);在 gc() 之前,但不建议这样做。还有一个问题,对于我们要做的终止验证,有没有更好的方法呢? @user2970539 有一个更好的方法,我在上面描述过:退出时显式检查条件。您甚至可以选择定义一个方法,例如Tank.checkExitConditions() 在应用程序退出之前显式调用。如果您的应用程序有多个退出点(例如代码中其他地方的System.exit()),您也必须在那里进行退出检查,尽管在这种情况下您可能需要考虑稍微清理一下设计并尝试仅退出从main返回。【参考方案2】:

根据JLS 12.6. Finalization of Class Instances

Java 编程语言没有指定多久会调用终结器,只是说它会在对象的存储被重用之前发生。

【讨论】:

以上是关于为啥不调用 finalize()? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

为啥在这个单元测试中没有调用 finalize 函数?

当我们显式调用 finalize() 时,对象内存是不是被释放? [复制]

服务端进程被杀掉,tcp连接处于FIN_WAIT2状态,为啥客户端调用socket发送数据还是成功的。

何时执行 finalize()? [复制]

为啥 java.lang.Object 中的 finalize() 方法是“受保护的”?

为啥隐式复制构造函数调用基类复制构造函数而定义的复制构造函数不调用?