📜  Java中的JVM的关闭挂钩

📅  最后修改于: 2020-02-10 15:13:31             🧑  作者: Mango

关闭挂钩是一种特殊的结构,允许开发人员插入要在JVM关闭时执行的代码。这在需要关闭VM的情况下需要执行特殊清理操作的情况下非常有用。
使用常规结构处理此问题,例如确保在应用程序退出之前调用特殊过程(调用System.exit(0))在虚拟机由于外部原因而关闭或资源问题(内存不足)的情况下(例如,kill请求),将不起作用(由于O / S)。正如我们将很快看到的,关闭关闭挂钩通过允许我们提供一个任意的代码块来轻松解决此问题,该代码块将在JVM关闭时被调用。
从表面上看,我们所要做的只是简单地编写一个扩展java.lang.Thread类的类,并提供要在VM关闭时在公共void run()方法内执行的逻辑。然后,通过调用Runtime.getRuntime()。addShutdownHook(Thread)方法,将此类的实例注册为JVM的关闭挂钩。如果需要删除以前注册的关闭挂钩,则Runtime类也提供removeShutdownHook(Thread)方法。
例如 :

public class ShutDownHook
{
  public static void main(String[] args)
  {
    Runtime.getRuntime().addShutdownHook(new Thread()
    {
      public void run()
      {
        System.out.println("Shutdown Hook is running !");
      }
    });
    System.out.println("Application Terminating ...");
  }
}

当我们运行上面的代码时,您将看到JVM在完成main方法的执行后会调用shutdown钩子。
输出:

Application Terminating ...
Shutdown Hook is running !

简单吧?是的!
虽然编写关闭挂钩非常简单,但是需要了解关闭挂钩的内部结构才能正确使用它们。因此,在本文中,我们将探讨关闭挂钩设计背后的一些“陷阱”。
1.在某些情况下,可能无法执行关机挂钩!
首先要记住的是,不能保证关闭挂钩将始终运行。如果JVM由于某些内部错误而崩溃,则它可能崩溃而没有机会执行一条指令。另外,如果操作系统给出SIGKILL(http://en.wikipedia.org/wiki/SIGKILL)信号(在Unix / Linux中为kill -9)或TerminateProcess(Windows),则要求应用程序立即终止而无需甚至在等待任何清理活动。除上述内容外,还可以通过调用Runtime.halt()方法来终止JVM,而无需运行关闭挂钩。
当应用程序正常终止时(所有线程结束或调用System.exit(0)时),将调用关闭挂钩。另外,当JVM由于诸如用户请求终止(Ctrl + C)之类的外部原因而关闭,由O / S发出的SIGTERM(正常kill命令,不带-9)或操作系统正在关闭时下,将调用关闭挂钩。
2.启动后,可以在完成之前强行关闭关闭挂钩。
这实际上是前面说明的情况的特例。尽管挂钩开始执行,但是在诸如操作系统关闭的情况下,有可能在挂钩完成之前将其终止。在这种情况下,一旦给出SIGTERM,O / S将等待进程终止指定的时间。如果该过程未在此期限内终止,则操作系统将通过发出SIGTERM(或Windows中的对应程序)来强制终止该过程。因此,当关闭挂钩执行到一半时,可能会发生这种情况。
因此,建议确保关闭挂钩的书写谨慎,以确保它们快速完成,并且不会引起死锁等情况。另外,JavaDoc [1]特别提到不应在关闭挂钩中执行长时间计算或等待用户I / O操作。
3.我们可以有多个关闭挂钩,但是不能保证它们的执行顺序。
您可能已经通过addShutdownHook方法的方法名称(而不是setShutdownHook)正确地猜到了,您可以注册多个关闭挂钩。但是,JVM无法保证这些多个挂钩的执行顺序。JVM可以按任意顺序执行关闭挂钩。此外,JVM可能会同时执行所有这些挂钩。
4.我们无法在关闭挂钩中注册/注销关闭挂钩,
一旦JVM启动了关闭序列,就不允许添加或删除任何现有的关闭挂钩。如果尝试这样做,则JVM会引发IllegalStateException。
5.关闭序列一旦开始,就只能由Runtime.halt()停止。
一旦关闭序列开始,只有Runtime.halt()(强制终止JVM)可以停止执行关闭序列(除了诸如SIGKILL之类的外部影响之外)。这意味着在关闭挂钩中调用System.exit()将不起作用。实际上,如果您在关闭挂钩中调用System.exit(),VM可能会卡住,我们可能不得不强制终止该过程。
6.使用关闭挂钩需要安全权限。
如果使用Java安全管理器,则执行添加/删除关闭挂钩的代码在运行时需要具有关闭挂钩权限。如果我们在安全的环境中未经许可调用此方法,则将导致SecurityException。
参考资料:http :
//docs.oracle.com/Javase/1.5.0/docs/api/Java/lang/Runtime.html#addShutdownHook(java.lang.Thread)