ShutdownHookがどのシグナルで呼び出されるか確認した

やりたいこと

JavaプログラムでaddShutdownHookに指定したThreadがどのシグナルで終了したときに呼び出されるのかを確認する。

Runtime (Java Platform SE 8)

確認方法

  1. 以下のJavaプログラムを実行する
  2. 起動したプログラムにkillコマンドでシグナルを投げて「call shutdown hook」が標準出力に出るか確認する。
public class SignalTest {
    public static void main(String [] args) {
        Runtime.getRuntime().addShutdownHook(new Thread(
            () -> System.out.println("call shutdown hook")
        ));
        while(true) {
            try {
                Thread.sleep(5000);
                System.out.println("exec...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

確認対象のシグナルは以下の4つ

  • SIGINT(2)
  • SIGQUIT(3)
  • SIGTERM(15)
  • SIGKILL(9)

確認環境

  • OS
  • Kernel
    • 4.4.0-119-generic
  • javac
    • javac 1.8.0_162
  • java
    • openjdk version "1.8.0_162"
    • OpenJDK Runtime Environment (build 1.8.0_162-8u162-b12-0ubuntu0.16.04.2-b12)
    • OpenJDK 64-Bit Server VM (build 25.162-b12, mixed mode)

確認結果

SIGINT(2)

呼び出された。

$ java SignalTest 
exec...
call shutdown hook
$ kill -2 pid

SIGQUIT(3)

呼び出されない。
Javadumpが出力され、処理が継続している。

$ java SignalTest 
exec...
exec...
exec...
exec...
exec...
2018-04-08 10:35:05
Full thread dump OpenJDK 64-Bit Server VM (25.162-b12 mixed mode):

"Service Thread" #9 daemon prio=9 os_prio=0 tid=0x00007f2b580c8000 nid=0x81e runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread3" #8 daemon prio=9 os_prio=0 tid=0x00007f2b580c3800 nid=0x81d waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread2" #7 daemon prio=9 os_prio=0 tid=0x00007f2b580bf000 nid=0x81c waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007f2b580bd000 nid=0x81b waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007f2b580ba000 nid=0x81a waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007f2b580b8000 nid=0x819 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007f2b58090800 nid=0x818 in Object.wait() [0x00007f2b1f0ce000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x000000071a208ec0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
        - locked <0x000000071a208ec0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:212)

"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007f2b5808c000 nid=0x817 in Object.wait() [0x00007f2b1f1cf000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x000000071a206b68> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked <0x000000071a206b68> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"main" #1 prio=5 os_prio=0 tid=0x00007f2b5800a800 nid=0x80b waiting on condition [0x00007f2b5fe1e000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at SignalTest.main(SignalTest.java:10)

"VM Thread" os_prio=0 tid=0x00007f2b58084000 nid=0x816 runnable 

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f2b5801f800 nid=0x80c runnable 

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007f2b58021800 nid=0x80d runnable 

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00007f2b58023000 nid=0x80e runnable 

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00007f2b58025000 nid=0x80f runnable 

"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x00007f2b58026800 nid=0x810 runnable 

"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x00007f2b58028800 nid=0x811 runnable 

"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x00007f2b5802a000 nid=0x812 runnable 

"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x00007f2b5802c000 nid=0x813 runnable 

"GC task thread#8 (ParallelGC)" os_prio=0 tid=0x00007f2b5802d800 nid=0x814 runnable 

"GC task thread#9 (ParallelGC)" os_prio=0 tid=0x00007f2b5802f000 nid=0x815 runnable 

"VM Periodic Task Thread" os_prio=0 tid=0x00007f2b580ca800 nid=0x81f waiting on condition 

JNI global references: 309

Heap
 PSYoungGen      total 148992K, used 7680K [0x000000071a200000, 0x0000000724800000, 0x00000007c0000000)
  eden space 128000K, 6% used [0x000000071a200000,0x000000071a980098,0x0000000721f00000)
  from space 20992K, 0% used [0x0000000723380000,0x0000000723380000,0x0000000724800000)
  to   space 20992K, 0% used [0x0000000721f00000,0x0000000721f00000,0x0000000723380000)
 ParOldGen       total 339968K, used 0K [0x00000005ce600000, 0x00000005e3200000, 0x000000071a200000)
  object space 339968K, 0% used [0x00000005ce600000,0x00000005ce600000,0x00000005e3200000)
 Metaspace       used 3490K, capacity 4632K, committed 4864K, reserved 1056768K
  class space    used 380K, capacity 459K, committed 512K, reserved 1048576K

exec...
exec...
$ kill -3 pid

SIGTERM(15)

呼び出された。

$ java SignalTest 
exec...
exec...
exec...
call shutdown hook
$ kill -15 pid

SIGKILL(9)

呼び出されない。
最後の「強制終了」はjvmの出力?と思われる。javadocの記述通りの挙動になった。

$ java SignalTest 
exec...
exec...
exec...
強制終了
$ kill -9 pid