shutdown hook vs finalizer方法

时间:2013-12-29 19:29:48

标签: java multithreading finalizer shutdown-hook

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

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

2 个答案:

答案 0 :(得分:21)

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

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

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

执行关闭挂钩 IS

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

关闭挂钩未执行

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

答案 1 :(得分:4)

关于您的查询

  

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

我从article

中找到了很好的信息
    在垃圾收集器回收对象之前调用
  1. finalize()。调用此方法时,JVM不保证。

  2. finalize()只能通过GC线程调用一次,如果object从finalize方法中恢复,则不会再次调用finalize。

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

  4. GC线程忽略了finalize方法引发的任何异常

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

  6. 按照oracle documentation

    返回shutdownHooks
      

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

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

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

        

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

           

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

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

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

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