javajvmjvm-hotspotshutdown-hook

How is VM_Exit delivered to VM Thread when sending SIGTERM and who does the delivering?


I'm trying to understand how JVM hotspot handles termination signals (SIGTERM for example). What I could found is that the SIGTERM signal disposition is set at this point to UserHandler which looks as (comments ommitted):

static void UserHandler(int sig, void *siginfo, void *context) {
  if (sig == SIGINT && Atomic::add(1, &sigint_count) > 1) {
    return;
  }
  if (sig == SIGINT && VMError::is_error_reported()) {
    os::die();
  }
  os::signal_notify(sig);
}

So all it does is to notify Signal Dispatcher and set the received signal number to the static volatile jint pending_signals[NSIG+1].

But in case of SIGTERM the actual exit(143) is done in VM Thread. The VM_Exit task with the _exit_code = 143 is somehow delivered to the VM Thread.

Question: Can you give a hint who generates this VM_Exit task and sends it to VM Thread later? I'm particularly concerned about how the 143 is set to VM_Exit::_exit_code?

I ran JVM HotSpot under gdb with the following main class:

public class Main{
    public static void main(String args[]) throws Exception {
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            System.out.println("Shutdown hook is called");
        }));
        Thread.sleep(1000000);
    }
}

And did not find that Signal Dispatcher was sending the VM_Exit task. The termination occured at that statement:

JavaCalls::call_virtual(&result,
                        threadObj, thread_klass,
                        vmSymbols::exit_method_name(),
                        vmSymbols::void_method_signature(),
                        THREAD);

Solution

  • SIGTERM, SIGINT and SIGHUP are handled in Java code of java.lang.Terminator.

    One of the lesser known features of async-profiler is that it can profile arbitrary native functions and show mixed Java+native stacks. E.g. if you want to intercept JVM_Halt and see what Java code calls it, run

    $ java -agentpath:/path/to/libasyncProfiler.so=start,traces,threads,event=JVM_Halt Main
    
    Started [JVM_Halt] profiling
    ^CShutdown hook is called
    --- 1 events (100.00%), 1 sample
      [ 0] JVM_Halt
      [ 1] java.lang.Shutdown.halt0
      [ 2] java.lang.Shutdown.halt
      [ 3] java.lang.Shutdown.exit
      [ 4] java.lang.Terminator$1.handle
      [ 5] jdk.internal.misc.Signal$1.run
      [ 6] java.lang.Thread.run
      [ 7] [SIGINT handler tid=19080]