13

我只是不明白为什么必须使用 Runtime.addShutdownHook。如果您想在 jvm 退出时进行一些清理,为什么不重载守护程序类的 finalize 方法。与 finalize 方法相比,使用关闭挂钩有什么优势。

还有一个已弃用的函数 runFinalizersOnExit。如果我将其设置为 false,我相信终结器将不会运行。这与 java 保证终结器总是在垃圾收集之前运行相矛盾。

4

2 回答 2

23

无法保证终结器将永远运行。finalize()当对象被垃圾回收时调用。但是当程序运行时,垃圾收集器可能不会收集任何东西。

相比之下,当 jvm 正常退出时会运行关闭挂钩。所以即使这也不是 100% 的保证,但它非常接近。只有少数情况下关闭挂钩不运行。

编辑 我查找了未执行关闭挂钩的边缘情况

已执行关闭挂钩

  • 当所有 JVM 线程都已完成执行时
  • 因为调用 System.exit()
  • 因为用户点击了 CNTRL-C
  • 系统级关闭或用户注销

执行关闭挂钩:

  • 如果 VM 由于本机代码中的错误而崩溃,则无法保证挂钩是否会运行。
  • 如果在 Linux 上使用 -kill 命令或在 Windows 上使用 Terminate Process 杀死 JVM,则 JVM 会立即退出
于 2013-12-29T19:40:53.850 回答
5

关于您的查询

如果你想在 jvm 退出时进行一些清理,为什么不重载 daemon 类的 finalize 方法

我从这篇文章中找到了很好的信息

  1. finalize()在垃圾收集器回收对象之前调用。JVM 不保证何时调用此方法。

  2. finalize()如果对象从 finalize 方法中恢复自身,则 GC 线程仅调用一次,而不是 finalize 将不再被调用。

  3. 在您的应用程序中,您可能有一些活动对象,它们永远不会调用垃圾收集。

  4. GC线程忽略finalize方法抛出的任何异常

  5. System.runFinalization(true)Runtime.getRuntime().runFinalization(true)方法增加了调用finalize()方法的概率,但现在这两种方法已被弃用。由于缺乏线程安全性和可能产生死锁,这些方法非常危险。

根据 oracle文档,回到 shutdownHooks

public void addShutdownHook(Thread hook) 注册一个新的虚拟机关闭钩子。

Java 虚拟机关闭以响应两种事件:

  1. 程序正常退出,当最后一个非守护线程退出或调用 exit(等效于 System.exit)方法时,或
  2. 虚拟机响应用户中断(例如键入 ^C)或系统范围的事件(例如用户注销或系统关闭)而终止。
  3. 当虚拟机开始其关闭序列时,它将以某种未指定的顺序启动所有已注册的关闭挂钩并让它们同时运行。当所有钩子都完成后,如果 finalization-on-exit 已启用,它将运行所有未调用的终结器。
  4. 最后,虚拟机将停止。请注意,在关闭序列期间,守护线程将继续运行,如果通过调用 exit 方法启动关闭,非守护线程也将继续运行。

但即使是甲骨文文档也引用了

关闭挂钩也应该快速完成它们的工作。当程序调用 exit 时,期望虚拟机将立即关闭并退出。

在极少数情况下,虚拟机可能会中止,即在没有干净关闭的情况下停止运行

考虑到这两种方法的缺点,您应该遵循以下方法

  1. 不要依赖finalize()shutdown hooks释放应用程序中的关键资源。

  2. 适当使用try{} catch{} finally{}块并在块中释放关键资源 finally(}。在finally{}块中释放资源期间,catchExceptionThrowable.

于 2015-12-26T14:29:01.293 回答