我不明白为什么必须使用Runtime.addShutdownHook。如果你想在jvm退出时进行一些清理工作,为什么不只是过载守护类的finalize方法呢?使用shutdown hook相比finalize方法有何优势?
此外还有一个已弃用的函数runFinalizersOnExit。如果将其设置为false,我相信finalizers将不会运行。这与Java保证finalizers始终在垃圾回收之前运行的规定相矛盾。
我不明白为什么必须使用Runtime.addShutdownHook。如果你想在jvm退出时进行一些清理工作,为什么不只是过载守护类的finalize方法呢?使用shutdown hook相比finalize方法有何优势?
此外还有一个已弃用的函数runFinalizersOnExit。如果将其设置为false,我相信finalizers将不会运行。这与Java保证finalizers始终在垃圾回收之前运行的规定相矛盾。
不能保证最终器(finalizer)一定会运行。当对象被垃圾收集器(garbage collector)回收时,会调用finalize()
方法。但是,程序运行时垃圾收集器可能不会回收任何东西。
相比之下,JVM正常退出时会运行关闭挂钩(shutdown hooks)。所以即使如此也不能百分之百地保证,但它非常接近。只有极少数情况下关闭挂钩不会运行。
编辑 我查看了关闭挂钩未执行的极端情况:
关闭挂钩将被执行:
关闭挂钩不会被执行:
关于您的问题
如果您想在jvm退出时进行一些清理,为什么不重载守护进程类的finalize方法呢?
我从这篇文章中找到了很好的信息。
finalize()
在Garbage Collector回收对象之前被调用。JVM不能保证何时调用此方法。
finalize()
只会被GC线程调用一次,如果对象从finalize方法中恢复,则不会再次调用finalize方法。
在您的应用程序中,可能有一些活动对象,垃圾收集器永远不会调用它们。
GC线程忽略由finalize方法抛出的任何异常。
System.runFinalization(true)
和Runtime.getRuntime().runFinalization(true)
方法增加了调用finalize()
方法的概率,但现在这两个方法已经过时了。这些方法非常危险,因为缺乏线程安全性和可能导致死锁。
回到shutdownHooks,根据Oracle文档:
public void addShutdownHook(Thread hook) 注册一个新的虚拟机关闭挂钩。
Java虚拟机响应两种事件而关闭:
但即使Oracle文档也引用:
关闭挂钩也应尽快完成其工作。当程序调用exit时,预期是虚拟机会立即关闭并退出。
在罕见情况下,虚拟机可能会中止,即在不干净地关闭的情况下停止运行。
考虑到这两种方法的缺点,您应该遵循以下方法:
不要依赖finalize()
或shutdown hooks
释放应用程序中的关键资源。
适当使用try{} catch{} finally{}
块,并在finally{}
块中释放关键资源。在释放finally{}
块中的资源时,捕获Exception
和Throwable
。
runFinalizersOnExit
。 - scottb