java进程关闭事件监听
jvm进程如何感知关闭事件
java.lang.Shutdown
结束一个普通的java进程,一般来说可以让程序自行结束,也可以通过System.exit(n);来主动触发终止。
如果是linux系统,还可以通过外部信号来终止进程。
一般来说停止一个服务常用的方式就是
kill -2 pid(ctrl + C)
、
kill -9 pid
、
kill -15 pid
kill -9 可以认为操作系统从内核级别直接强行kill进程,对进程来说没有任何的准备,且无法监听-9信号。
kill -2 和 -15 则是操作系统给该进程发送一个信号通知,告知应用主动关闭,应用可以监听并接收到信号,可以完成一些关闭回收等动作,然后自我停止。
jvm专门有个
Signal Dispatcher
线程来接收信号。
在
Shutdown
针对这几类终止方式提供了两个处理方法。
/* Invoked by the JNI DestroyJavaVM procedure when the last non-daemon thread has finished.
Unlike the exit method, this method does not actually halt the VM. */
static void shutdown() {
synchronized (lock) {
switch (state) {
case RUNNING: /* Initiate shutdown */
state = HOOKS;
break;
case HOOKS: /* Stall and then return */
case FINALIZERS:
break;
synchronized (Shutdown.class) {
sequence();
结合方法注释,当前进程如果所有的非守护线程执行完成,会由JNI DestroyJavaVM触发shutdown方法调用。此方法并没有halt(停止)VM。
/* Invoked by Runtime.exit, which does all the security checks.
* Also invoked by handlers for system-provided termination events, which should pass a nonzero status code.
static void exit(int status) {
boolean runMoreFinalizers = false;
synchronized (lock) {
if (status != 0) runFinalizersOnExit = false;
switch (state) {
case RUNNING: /* Initiate shutdown */
state = HOOKS;
break;
case HOOKS: /* Stall and halt */
break;
case FINALIZERS:
if (status != 0) {
/* Halt immediately on nonzero status */
halt(status);
} else {
// Compatibility with old behavior: Run more finalizers and then halt
runMoreFinalizers = runFinalizersOnExit;
break;
if (runMoreFinalizers) {
runAllFinalizers();
halt(status);
synchronized (Shutdown.class) {
//Synchronize on the class object, causing any other thread that attempts to initiate shutdown to stall indefinitely
sequence();
halt(status);
当在代码中调用了Runtime.exit(System.exit),会调用此exit方法,status
是0表示正常退出,非0表示异常退出,此方法会主动halt VM。
除了进程退出的处理方法外,在ShutDown类中,还定义了hook,允许我们在进程停止前,完成一些清场的操作。
// The system shutdown hooks are registered with a predefined slot.
// The list of shutdown hooks is as follows:
// (0) Console restore hook
// (1) Application hooks
// (2) DeleteOnExit hook
private static final int MAX_SYSTEM_HOOKS = 10;
private static final Runnable[] hooks = new Runnable[MAX_SYSTEM_HOOKS];
每个hook对应一个线程,会在add
方法中添加,在runHooks
中依次执行。
static void add(int slot, boolean registerShutdownInProgress, Runnable hook) {
synchronized (lock) {
if (hooks[slot] != null)
throw new InternalError("Shutdown hook at slot " + slot + " already registered");
// ... 异常场景判断
hooks[slot] = hook;
/* Run all registered shutdown hooks */
private static void runHooks() {
for (int i=0; i < MAX_SYSTEM_HOOKS; i++) {
try {
Runnable hook;
synchronized (lock) {
// acquire the lock to make sure the hook registered during shutdown is visible here.
currentRunningHook = i;
hook = hooks[i];
if (hook != null) hook.run();
} catch(Throwable t) {
if (t instanceof ThreadDeath) {
ThreadDeath td = (ThreadDeath)t;
throw td;
如何添加自定义的shutdown hook
Shutdown
类属于系统的操作类,并没有暴露给应用层使用。如果我们想定义自己的shutdown hook,可以使用Runtime.getRuntime().addShutdownHook()
public void addShutdownHook(Thread hook) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("shutdownHooks"));
ApplicationShutdownHooks.add(hook);
这个方法调用了ApplicationShutdownHooks.add(hook)
ApplicationShutdownHooks
class ApplicationShutdownHooks {
private static IdentityHashMap<Thread, Thread> hooks;
static {
try {
Shutdown.add(1 /* shutdown hook invocation order */,
false /* not registered if shutdown in progress */,
new Runnable() {
public void run() {
runHooks();
hooks = new IdentityHashMap<>();
} catch (IllegalStateException e) {
// application shutdown hooks cannot be added if
// shutdown is in progress.
hooks = null;
/* Add a new shutdown hook. Checks the shutdown state and the hook itself, but does not do any security checks. */
static synchronized void add(Thread hook) {
if(hooks == null)
throw new IllegalStateException("Shutdown in progress");
if (hook.isAlive())
throw new IllegalArgumentException("Hook already running");
if (hooks.containsKey(hook))
throw new IllegalArgumentException("Hook previously registered");
hooks.put(hook, hook);
// ...
ApplicationShutdownHooks
在类初始化过程完成了hook回调的注册,并初始化了IdentityHashMap,当有自定义的hook被添加时,缓存到map中。
当shutdown被触发后,会通过hook回调来调用到runHooks()
/* Iterates over all application hooks creating a new thread for each
* to run in. Hooks are run concurrently and this method waits for
* them to finish.
static void runHooks() {
Collection<Thread> threads;
synchronized(ApplicationShutdownHooks.class) {
threads = hooks.keySet();
hooks = null;
for (Thread hook : threads) {
hook.start();
for (Thread hook : threads) {
while (true) {
try {
hook.join();
break;
} catch (InterruptedException ignored) {
runHooks()
会遍历启动的每一个hook线程,并通过join
来等待所有hook执行完成。因为这个hook线程是并行操作的,所以这里无法保证hook的执行顺序。
jvm进程的关闭会由JNI触发Shutdown
类中的exit()
或shutdown()
, 这两个方法会调用hook回调。
自定义的shutdown hook通过Runtime.getRuntime().addShutdownHook()
添加到进程中。
多个自定义shutdown hook并行执行,不保证执行顺序。