JVM 终止时会发生什么?

What happens when the JVM is terminated?

当 JVM 以 System.exit(0)^C 或类似的方式终止时会发生什么?我读到诸如“进程刚刚被吹走”和“每个线程都停止”之类的内容,但我想知道到底发生了什么。我已经知道 shutdownHook 仍然以某种方式执行,但是在调用 shutdownHooks 之前会发生什么,在所有这些线程完成后会发生什么?

我想正确地实施这样的 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();
    }
}

map 函数计算在其他某个对象中收集的结果。这个对象应该在所有东西被分解之前存储在某个文件中(也被普通优先级shutdownHooks)。这里的 shutdownHook 有意义吗?据我了解,所有线程都首先被销毁,然后 shutdownHook 才被销毁 运行 (同时,但我假设高优先级线程首先是 运行 ...)然后对象被最终确定。这使得上面的代码相当无用,因为这个 shutdownHook 的目的是确保在关闭已经开始时没有新的循环开始。我的理解是否正确和完整?

看看这个来自 DZone 的 article。它详细介绍了 JVM 及其终止,重点是 ShutdownHook.

的使用

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

  1. Shutdown Hooks may not be executed in some cases!
  2. Once started, Shutdown Hooks can be forcibly stopped before completion.
  3. You can have more than one Shutdown Hook, but their execution order is not guaranteed.
  4. You cannot register / unregister Shutdown Hooks with in Shutdown Hooks
  5. Once shutdown sequence starts, it can be stopped by Runtime.halt() only.
  6. Using shutdown hooks require security permissions.
  7. Exceptions thrown by the Shutdown Hooks are treated same as exceptions thrown by any other code segment.

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

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

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

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

Shutdown class 是 java.lang 中的内部包私有 class。其中,它有一个 exit() 和一个 halt() 方法。它的 exit() 方法做了一些事情来防止钩子被执行两次,等等,但基本上,它所做的是

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

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

A shutdown hook is simply an initialized but unstarted thread. When the virtual machine begins its shutdown sequence it will start all registered shutdown hooks in some unspecified order and let them run concurrently. When all the hooks have finished it will then run all uninvoked finalizers if finalization-on-exit has been enabled. Finally, the virtual machine will halt. Note that daemon threads will continue to run during the shutdown sequence, as will non-daemon threads if shutdown was initiated by invoking the exit method.

(强调我的)

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

您的另一个误解是赋予线程高优先级。高优先级并不意味着该线程将 运行 首先,在所有其他挂钩之前。这仅意味着每当操作系统必须决定将处于 "ready to run" 状态的线程中的哪一个交给 CPU 到 运行 时,高优先级线程将具有"winning" 的概率更高 - 取决于操作系统的调度算法。简而言之,它可能会获得更多 CPU 访问权限,但它不会 - 特别是如果您有多个 CPU 核心 - 必须在其他线程之前开始或在它们之前完成。

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