关闭钩子与终结器方法

我只是不明白为什么必须使用Runner.addShutdownHook。如果你想在 jvm 退出时做一些清理,为什么不重载守护进程类的 finalize 方法。使用关闭挂钩而不是finize方法的优势是什么?

还有一个不推荐使用的函数runFinalizersOnExit。如果我将其设置为 false,我相信终结器不会运行。这与 Java 保证结尾器始终在垃圾回收之前运行相矛盾。


答案 1

无法保证终结器运行。 在对对象进行垃圾回收时调用。但是,当程序运行时,垃圾回收器可能不会收集任何内容。finalize()

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

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

正在执行关闭钩子:

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

执行关机挂钩:

  • 如果 VM 由于本机代码中的错误而崩溃,则无法保证是否运行挂接。
  • 如果在 Linux 上使用 -kill 命令杀死 JVM 或在 Windows 上使用终止进程,则 JVM 会立即退出

答案 2

关于您的查询

如果你想在 jvm 退出时做一些清理,为什么不重载守护进程类的 finalize 方法呢?

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

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

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

  3. 在您的应用程序中,您可能有一些活动对象,从不调用垃圾回收。

  4. 任何由 finalize 方法引发的异常都会被 GC 线程忽略

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

回到关闭Hooks,根据oracle文档

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

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

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

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

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

在极少数情况下,虚拟机可能会中止,即停止运行而不完全关闭

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

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

  2. 适当地使用块,并在块中释放关键资源。在释放块、捕获和中的资源期间。try{} catch{} finally{}finally(}finally{}ExceptionThrowable