【问题标题】:contextDestroyed() vs addShutdownHook()contextDestroyed() 与 addShutdownHook()
【发布时间】:2011-08-05 01:10:15
【问题描述】:
【问题讨论】:
标签:
java
application-shutdown
servlet-listeners
【解决方案1】:
我认为 ServletContextListener 更适合 Web 应用程序,因为您为每个会话清理资源。
在 JVM 关闭的情况下执行关闭挂钩。那将是您停止容器时,这是一次性事件。
【解决方案2】:
使用 addShutdownHook() 的危险在于您可能会遇到类加载器泄漏,当您多次重新部署应用程序时,这种泄漏会变得很明显。
因为关闭钩子的类(线程子类或 webapp 中的 Runnable 实现)来自 webapp 的类加载器,即使在容器取消部署 webapp 之后,关闭钩子仍将注册到系统。这意味着整个 webapp 的类加载器不能被垃圾回收。
我肯定会推荐 ServletContextListener。
【解决方案3】:
许多 servlet 容器支持在不关闭 JVM 进程的情况下动态删除和/或重新加载 WAR 的操作。因此,如果您将清理例程编写为 ServletContextListener,则它可能会在容器的生命周期内运行多次。 (例如,如果您在容器进程仍在运行时多次修改并重新加载 WAR。)
但是,如果您使用 Runtime.addShutdownHook 实现清理,它将只运行一次:当整个容器的 JVM 关闭时。
ServletContextListener 可能是您的正确答案,因为它将您的清理例程与 Web 应用程序的生命周期相结合,而不是托管它的容器进程的生命周期。
【解决方案4】:
我们所说的泄漏只有在我们进行热/部署时才会发生。但是,如果在每次部署更改后重新启动服务器,那么钩子应该可以正常工作而不会发生内存泄漏。
此外,控制泄漏的另一个因素是您试图通过侦听器/挂钩控制的资源清理类型。
【解决方案5】:
为什么不两者都做?虽然 ServletContextListener 更适合 webapp,但我发现在开发过程中服务器经常突然停止,然后 contextDestroyed() 永远不会被调用,因此您可以同时使用这两种机制来确保始终正常关闭:
实现一个 ServletContextListener,其中 contextInitialized 调用 addShutdownHook() 和 contextDestroyed 调用 removeShutdownHook()。 hook 和 contextDestroyed 都可以委托给一些内部方法来实际进行清理。
这样,如果正确调用了侦听器,则添加和删除挂钩(但未调用)并且没有泄漏,但如果服务器在没有销毁上下文的情况下死亡,则关闭挂钩会清理所有内容。