shutdown hook vs finalizer方法

S K*_* Kr 12 java multithreading finalizer shutdown-hook

我只是不明白为什么必须使用Runtime.addShutdownHook.如果你想在jvm退出时进行一些清理,为什么不重载守护进程类的finalize方法.使用shutdown hook而不是finalize方法有什么好处.

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

dka*_*zel 21

没有一个终结将保证永远运行.finalize()在对象被垃圾收集时调用.但是,当程序运行时,垃圾收集器可能无法收集任何内容.

当jvm正常退出时,运行对比关闭钩子.所以,即使这不是100%保证,但它非常接近.只有少数边缘情况不会运行关闭挂钩.

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

关闭挂钩IS执行:

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

关闭挂钩IS NOT执行:

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

  • @SKr不,我正在考虑正常关机.最后一个线程结束 - > VM退出.没有GC.没有终结者. (3认同)

Rav*_*abu 5

关于您的询问

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

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

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

  2. finalize()如果对象通过 Finalize 方法恢复自身,则 GC 线程仅调用一次,而不会再次调用 Finalize。

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

  4. Finalize 方法抛出的任何异常都会被 GC 线程忽略

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

回到 shutdownHooks,根据 oracle文档

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

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

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

但即使是 oracle 文档也引用了这一点

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

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

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

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

  2. 适当地使用try{} catch{} finally{}块并释放块中的关键资源 finally(}finally{}在block、catchException和中释放资源期间Throwable