当 JVM 被终止时会发生什么情况?

2022-09-02 10:33:13

当 JVM 以 或 或类似的任何内容终止时会发生什么?我读到诸如“该过程被吹走了”和“每个线程都被停止”之类的东西,但我想知道究竟发生了什么。我已经知道仍然以某种方式执行,但是在调用关闭Hooks之前会发生什么,并且在所有这些线程完成后会发生什么?System.exit(0)^CshutdownHook

我想正确地实现这样一个,为此,我需要对哪些可能仍然执行,什么不会执行做出正确的假设。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<Object> it) {
        while(it.hasNext() && !stopped) {
            writeToOtherObject(it.next());
            it.remove();
        }
        //have calculations finished?
        return !it.hasNext();
    }
}

该函数计算在某个其他对象中收集的结果。在分解所有内容之前,此对象应存储在某个文件中(也按正常优先级)。这里有意义吗?据我所知,所有线程首先被销毁,然后s被运行(并发,但我假设高优先级线程首先运行...),然后对象被最终确定。这使得上面的代码相当无用,因为这样做的目的是确保在关机已经开始时没有启动新的循环。我的理解是否正确和完整?mapshutdownHookshutdownHookshutdownHookshutdownHook


答案 1

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

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

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

如果您中断了 JVM 或系统向它发送了 TERM 信号,那么缺省情况下,将直接调用 TERM 信号,而无需与安全管理器进行核对。Shutdown.exit()

该类是 中的内部包私有类。除其他方法外,它还具有方法。它的方法做了一些事情来防止钩子被执行两次,依此类推,但基本上,它所做的是Shutdownjava.langexit()halt()exit()

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

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

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

(强调我的)

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

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

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


答案 2

看看DZone的这篇文章。它足够详细地介绍了 JVM 及其终止,重点是 .ShutdownHook

从高层次上讲,它所涵盖的一些重要假设包括:

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

推荐