【问题标题】:What is the use of overriding the finalize() method of Object class?重写 Object 类的 finalize() 方法有什么用?
【发布时间】:2011-04-12 13:12:59
【问题描述】:

据我所知,在 java 中,如果我们想手动调用垃圾收集器,我们可以使用 System.gc()。
1.我们在覆盖的 finalize() 方法中做了哪些操作?
2.如果要手动调用JVM垃圾收集器,是否需要重写finalize()方法?

【问题讨论】:

  • 很可能您永远不需要 finalize(),除了记录以跟踪泄漏之外,请记住 finalize() 会(相当大地)减慢对象并需要正确实现同步。
  • 注意你的finalize()方法,如果被调用,只会被调用一次。
  • 这里假设 System.gc() 允许手动 GC。实际上它不是“手动”的,因为 JVM 可能会或不会遵循这个请求。此调用只是对 VM 的提示。

标签: java garbage-collection


【解决方案1】:

我们在覆盖的 finalize() 方法中做了哪些操作?

手动分配的空闲内存(通过一些本机调用),即不由 GC 管理。这是一种非常罕见的情况。 有些人还在那里检查了与该对象相关的其他资源是否已被释放 - 但它仅用于调试目的并且不是很可靠。
你必须记住:

  1. finalize 是关于记忆的,只有记忆。
  2. 您无法控制何时调用它,甚至是否调用它。

所以永远不要放在那里释放任何其他资源(如文件句柄、锁)。所有这些资源都必须手动释放。
并且覆盖 finalize 与调用 System.gc() 无关。

【讨论】:

    【解决方案2】:

    其他人已经解释了finalize方法。

    但是请注意,最佳实践是设计一个“关闭”/“清理”/“终止”方法来关闭/清理对象并要求开发人员明确调用此方法。终结器只能用作安全网。 (参见 Joshua Bloch 的“Effective Java”的“Item 7: Avoid finalizers”)

    更新:

    在这种情况下,还可以考虑实现 AutoCloseable 以支持 try-with-resources 块。

    【讨论】:

      【解决方案3】:

      如果您需要手动释放资源(尤其是那些会使用大量内存或导致锁定的资源(如果未释放)),通常会覆盖 finalize 方法。例如,如果您的对象在文件或数据库表上具有锁,您可能希望覆盖您的 finalize 方法,以便在您的对象被垃圾回收时释放这些对象以供使用。

      至于是否需要覆盖finalize,绝对不需要。您可以调用 System.gc() 而无需覆盖 finalize 方法。不过要注意一点——调用 System.gc() 并不绝对表明垃圾收集器会做任何事情。将其视为对 JVM 运行垃圾收集的“建议”,而不是“授权”。

      【讨论】:

      • 我唯一要补充的是,您应该尽可能努力释放所有资源,而不要使用finalize(),因为它会损害性能,并且不能保证它何时会真正得到叫
      • 我同意亚当的观点有几个原因。确实,使用 finalize 会损害你的性能,但它很难使用,因为你永远不知道它什么时候会被调用。垃圾收集按自己的时间表完成,即使您调用 System.gc(),您也不能强制它发生。因此,在 finalize 方法中释放资源可能会导致各种奇怪的事情发生,因为跟踪应用程序的流程更加困难。我强烈建议尽快、尽可能明确地发布资源,以使您的生活更轻松。
      • 非常感谢您的回复...如果我在我的类中覆盖 finalize() 那么如何调用该方法?或者 JVM 本身在垃圾收集时会调用我覆盖的 finailze()??
      • 您可以像调用任何其他实例方法一样调用它。也就是说,finalize 方法背后的全部目的是您不必调用它。当 JVM 执行其垃圾收集过程时,它会在它打算进行垃圾收集的每个对象上调用 finalize。 JVM 永远只会调用一次你的 finalize 方法。但是,如果您直接调用它,则可以根据需要多次调用它。如果您打算自己调用该方法,请不要使用 finalize - 只需创建一个方法并使用它。如果您想利用垃圾收集器,请使用 finalize。
      【解决方案4】:

      在极少数情况下,实现finalize 以释放分配的资源可能有用或有必要。这个方法总是在实例最终被销毁时调用。

      如果您使用本机代码并需要在本机代码区域释放分配的资源,这可能会有所帮助。

      但请谨慎使用,因为实现finally 会显着降低垃圾收集器的速度并降低性能。

      【讨论】:

      • Finalize 减慢分配速度(在调用 c-tor 之前将对象添加到链表中),而不是垃圾收集。 finalize() 也在单独的线程中执行。
      【解决方案5】:

      1) 您想要对对象销毁进行的任何清理(例如,如果您保持一些句柄打开并且没有人调用显式关闭)。请记住,您不知道何时会发生这种情况,因为 JVM 会调用 GC 而不是您。

      2) 不。

      旁注,您不应该手动调用垃圾收集器,请在此处查看答案,Why do you not explicitly call finalize() or start the garbage collector?

      【讨论】:

      • 我一直听说不能保证 finalize() 会被调用,但是 Java 不会为长时间运行的程序中的对象调用 finalize() 的可能性有多大,除了可能的情况JVM 在它解决之前在哪里关闭? ...或者这应该是一个单独的问题?
      • 它至少会被调用一次。好吧,我去年向 JBoss 报告了一个错误,因为我的文件句柄用完了。他们在 finalize() 方法中关闭了他们的文件资源,而我在调用 finalize 之前就用完了。它归结为应用程序大小、内存设置和行星的对齐方式。请记住,它可能不会发生,也许明确关闭会更好。
      • 没有。它可能永远不会被调用。在finalize 方法中释放句柄是一个编程错误。当不再需要它们时,应该手动释放它们(try finally 用于清理资源)
      • @Tadeusz 哇,实际上你是对的。通过 java.lang.Shutdown,您必须明确告诉 JVM 让终结器在退出时运行。所以他们可能根本不会被调用!
      • 上次 GC 和关机之间的那些不会运行。请参阅 System.runFinalizersOnExit() javadoc,他们曾经允许这样做,但现在标记为不安全,因此已弃用。
      【解决方案6】:

      无法保证会立即调用终结器,因此您不应在终结方法中执行任何关键操作。

      1. 如果系统资源(如 IOStreams)尚未关闭或释放,请将其作为安全网释放。
      2. 在 GC 不知道的本机代码中释放系统资源。

      【讨论】:

        【解决方案7】:

        @Andreas_D:我同意礁的说法,他的意思是,如果 finalize() 包含使当前实例不符合 GC 条件的代码,即通过为活动线程提供对该实例的引用来使其恢复活力,那么下次该实例符合 GC 条件时,不要期望 JVM 再次调用 finalize()。就 JVM 而言,它通过调用 finalize() 完成了它的职责,并且不会因为你不断地让实例再次恢复生机而费心地一次又一次地这样做。如果是这样,那么该对象将永远不会被 GC 处理。 (当然,您可能希望实例永远不会被 GC。然后让一个活动线程维护对它的引用。如果没有活动线程对实例的引用,那么无论如何,该实例就像死了一样好。)

        【讨论】:

          【解决方案8】:

          1.我们在覆盖的 finalize() 方法中做了哪些操作?

          清理垃圾回收无法处理的资源。

          2.如果要手动调用JVM垃圾收集器,是否需要重写finalize()方法?

          不,这两件事无关。

          finalize 当然不应该调用垃圾收集器本身。事实上,finalize 是由垃圾收集器调用的(但根本不保证会被调用)。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-06-26
            • 1970-01-01
            • 1970-01-01
            • 2018-01-01
            相关资源
            最近更新 更多