JVM终止后会发生什么?

我在互联网上四处寻找这个问题的答案,但我必须得出结论,我必须要么在互联网上找东西,要么在错误的地方找东西。

现在我希望有人可以帮助我解决我的问题:

当JVM以System.exit(0)^C或类似的东西终止时会发生什么? 我到处读到诸如“流程刚被吹走”和“每个线程都停止”之类的事情,但我想知道究竟发生了什么。 我已经知道有一些shutdownHook仍然会被执行,但之前发生了什么,并且在所有这些线程完成之后还有什么吗?

我的问题的真正原因是因为我想正确地实现这样的shutdownHook并对可能仍然执行的内容做出正确的假设。


更新:

一些代码:

 class SomeObject { private boolean stopped; SomeObject() { stopped = false; Thread hook = new Thread() { @Override public void run() { stopped = true; } }; hook.setPriority(Thread.MAX_PRIORITY); Runtime.getRuntime().addShutdownHook(hook); } boolean map(Iterator it) { while(it.hasNext() && !stopped) { writeToOtherObject(it.next()); it.remove(); } //have calculations finished? return !it.hasNext(); } } 

map函数计算在某个其他对象中收集的结果。 在所有内容都被分解之前,该对象应存储在某个文件中(通过普通优先级shutdownHook也是如此)。 shutdownHook在这里有意义吗? 至于我现在理解它,我假设所有线程都被破坏,然后才运行shutdownHook (同时,但我假设首先运行高优先级线程……)然后最终确定对象。 这使得上面的代码相当无用,因为这个shutdownHook的目的恰恰是确保在关闭已经开始时没有启动新的循环。 我理解得好,还是忘记了什么?

让我们从可以启动关闭序列的不同方式开始:

  • 最后一个非守护程序线程结束。
  • JVM被中断(通过使用ctrl C或发送SIGINT)。
  • JVM终止(通过发送SIGTERM)
  • 其中一个线程调用System.exit()Runtime.exit()

调用System.exit(int) ,它调用Runtime.exit() 。 它检查安全管理器是否允许以给定状态退出,如果是,则调用Shutdown.exit()

如果您中断了JVM或系统向其发送了TERM信号,则默认情况下,直接调用Shutdown.exit()而不检查安全管理器。

Shutdown类是java.lang的内部包私有类。 它包括exit()halt()方法。 它的exit()方法做了一些事情来防止钩子被执行两次,依此类推,但基本上,它的作用是什么

  1. 运行系统挂钩。 系统挂钩由JRE方法在内部注册。 它们按顺序运行,而不是在线程中运行。 第二个系统挂钩运行您添加的应用程序挂钩。 它将每个作为一个线程启动,然后在最后为每个线程提供一个join 。 其他系统挂钩可以在应用程序挂钩之前或之后运行。
  2. 如果终结者应该在停止之前运行,那么它们就会被运行。 这通常不会发生,因为该方法已被弃用。 如果退出的状态不是零,则无论如何都会忽略runFinalizersOnExit
  3. JVM停止了。

现在,与您的假设相反,在第3阶段,所有线程都停止了。 halt方法是本机的,我没有尝试读取本机代码,但是直到它被调用的那一刻,运行的唯一代码是纯Java,并且没有任何东西可以阻止其中的任何位置的线程。 Runtime.addShutdownHook的文档实际上说:

关闭钩子只是一个初始化但未启动的线程。 当虚拟机开始其关闭序列时,它将以某种未指定的顺序启动所有已注册的关闭挂钩,并让它们同时运行。 当所有钩子都完成后,如果启用了终止退出,它将运行所有未被发送的终结器。 最后,虚拟机将停止。 请注意,守护程序线程将在关闭序列期间继续运行,如果通过调用exit方法启动关闭,则非守护程序线程也将继续运行。

(强调我的)

所以你看,它确实是关闭钩子的工作的一部分,告诉线程他们应该离开他们的循环并清理。

另一个误解是关于给线程一个高优先级。 高优先级并不意味着线程将在所有其他挂钩之前首先运行。 它只是意味着每当操作系统必须决定哪些线程处于“准备运行”状态以给CPU运行时,高优先级线程将具有更高的“获胜”概率 – 取决于关于操作系统的调度算法。 简而言之,它可能会获得更多的CPU访问权限,但它不会 – 尤其是如果您有多个CPU核心 – 必须在其他线程之前启动或在它们之前完成。

最后一件事 – 如果你想使用一个标志告诉一个线程停止工作,那个标志应该是volatile

看看DZone的这篇文章 。 它详细介绍了JVM及其终止,重点介绍了ShutdownHook的使用。

从较高层面来看,它涵盖的一些重要假设包括:

  1. 在某些情况下可能无法执行关机挂钩!
  2. 一旦启动,可以在完成之前强制停止关机挂钩。
  3. 您可以拥有多个Shutdown Hook,但不保证其执行顺序。
  4. 您无法在Shutdown Hooks中注册/取消注册Shutdown Hooks
  5. 一旦关闭序列启动,它只能由Runtime.halt()停止。
  6. 使用关闭挂钩需要安全权限。
  7. Shutdown Hooks抛出的exception与任何其他代码段抛出的exception一样。