Literate JVM safe exit mechanism: shutdownHook, signalHandler

1. Background

The Java service running online always exits, and it is very frequent (think about how many times a day the service is released, every time the JVM is released, it will exit and restart or simply start it on another machine).

Then think about it, what to do if the following problems occur when the JVM exits:

  • At this time, if there are still asynchronous tasks in execution, what about these tasks?
  • The file is being written, and the JVM exits halfway through writing, which will cause the file to be damaged or incomplete
  • The data in the cache has not been persisted to disk, resulting in data loss
  • ... ...

If you have these problems, you should consider the safe exit of the JVM: do some aftermath work when the JVM exits.

Keywords: JVM safe exit; shutdownHook; signalHandler

2. JVM safe exit scenario

There are three types of scenarios for JVM exit, as follows:

In these three types of scenarios, JVM can perceive normal shutdown and abnormal shutdown. You can do some aftermath work through ShutdownHook or SignalHandler.

If you force the JVM to close, you will not be aware of it, and you will not be able to do the aftermath work. It is difficult to predict what impact will be caused after exiting, so we do not recommend using kill -9 to close the program.

3. kill command

First understand the command to exit the JVM.

After the service is released online, there must be no IDEA close button for us to use, let us close the program. Therefore, the JVM process is closed through the kill command.

So let's first understand the kill command.

Use this button to close the JVM program locally, and the online environment can only be closed by using the command line kill.

The normal usage of kill is: kill -signal_number pid. For example, if I want to forcibly close the process whose process id (pid) is 49736, execute: kill -9 49736.

Commonly used signal_numbers are as follows, among which 2, 9, and 15 are the most used

  • 1 HUP (hang up): hang up the thread
  • 2 INT (interrupt): interrupt thread (same as ctrl + c)
  • 3 QUIT (quit): Exit the thread (same as ctrl + \)
  • 9 KILL (non-catchable, non-ignorable kill): Forced termination, use with caution online
  • 15 TERM (software termination signal): normal termination
  • 19 STOP: suspend thread (same as ctrl+z)

4. SignalHandler

Java provides SignalHandler to monitor kill signal. We can do the aftermath logic in SignalHandler.

For example, let’s look at the following code, which is a signal processor that listens to the INT (kill -2) signal

 
 

java

copy code

public static void main(String[] args) throws InterruptedException { Signal sig = new Signal("INT"); // kill -2 ${pid} Signal.handle(sig, (s) -> { System.out.println("Signal handle start"); try { TimeUnit.SECONDS.sleep(3); // 模拟善后逻辑,阻塞3秒 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Signal handle end"); System.exit(0); // 这里显示调用 exit 退出程序,如果不调用,JVM 实例不会退出,这段逻辑就相当于一个普通的信号处理器 }); Thread.sleep(6000000); }

After the program starts, we open the command line tool and locate the pid of the JVM process through jps. Then execute the kill command: kill -2 ${pid}.

It will be found that the program triggers the logic in SignalHandler, executes System.exit(0) after blocking for 3 seconds, and the program exits.

If we execute other kill commands, the program will not have any special actions and terminate normally.

In addition, SignalHandler cannot monitor the KILL (kill -9) signal, and will report an error.

 
 

java

copy code

Signal sig = new Signal("KILL"); // 执行就会抛异常: Exception in thread "main" java.lang.IllegalArgumentException: Signal already used by VM or OS: SIGKILL at sun.misc.Signal.handle(Signal.java:166) at cn.edu.jxau.blog.Main.main(Main.java:19)

Therefore, we can use SignalHandler to monitor the kill signal (INT or TERM is generally used online) to do aftermath logic, so that the JVM can exit safely

5. ShutdownHook

To safely exit the JVM, in addition to SignalHandler, ShutdownHook can also be used.

The trigger condition of SignalHandler is that the corresponding kill signal is registered, and it is triggered only when there is a signal. ShutdownHook has more tricks. It is essentially a hook for JVM exit, which will be triggered as long as it exits. ShutdownHook will be triggered in the following scenarios:

  • System.exit(0);
  • IDEA closed manually
  • Command line: ctrl+c (note, ctrl+z will not trigger shutdownhook)
  • Command line: kill -2 signal
  • Command line: kill -15 signal
  • JVM exits abnormally (OOM or something)

Here is a chestnut that the JVM exited abnormally.

The code is as follows, first register a shutdownHook, and then a for loop, the simulation program runs halfway and throws the case of OutOfMemoryError.

 
 

java

copy code

public static void main(String[] args) throws InterruptedException { // 注册 shutdown hook Runtime.getRuntime().addShutdownHook(new Thread(() -> { System.out.println("shutdownHook start"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("shutdownHook end"); })); for (int i = 0; i <= 5; i++) { if (i == 5) { throw new OutOfMemoryError("123"); } Thread.sleep(1000); System.out.println("processing..."); } }

The console log is as follows. You can see that OutOfMemoryError is thrown and shutdownHook is also triggered.

Therefore, we can also use ShutdownHook to do aftermath logic, so that the JVM can exit safely.

So which of these two should you use? It is recommended to use ShutdownHook. Because ShutdownHook can respond to more exit scenarios, such as abnormal exit, System.exit(), etc., will trigger ShutdownHook instead of SignalHandler.

6. Precautions

ShutdownHook is used a lot, let's see what are the precautions.

  • The internal logic of shutdownHook cannot call System.exit(), otherwise the program will be stuck
  • When there are multiple shutdownHooks, these shutdownHooks are called concurrently, and the sequence of execution is not guaranteed
  • The JVM process closed by kill -9 will not trigger shutdownHook, nor can it be captured by SignalHandler

7. Summary

There are three types of JVM exit scenarios: normal exit, abnormal exit, and forced exit.

Java provides two sets of APIs for aftermath logic of JVM exit: SignalHander and ShutdownHook. Because ShutdownHook can deal with more scenarios, it is recommended to use ShutdownHook to do related logic.

I've seen this, give it a thumbs up

8. Ref

Guess you like

Origin blog.csdn.net/BASK2312/article/details/131210493