Startup, shutdown and exit codes

JVM · lesson 17 of 34 · 3 min read

main() returning is not the end. The JVM runs until the last non-daemon thread stops.

Open this lesson in the learning hub

Key points

  • The JVM exits when the last non-daemon thread finishes, not when main returns. One forgotten thread keeps it alive.
  • A daemon thread never holds the JVM open. It is killed wherever it happens to be, so never put work that must finish on one.
  • Runtime.addShutdownHook registers a thread to run on exit. Hooks run in parallel, in no order, and usually get only seconds.
  • System.exit(n) runs the hooks and leaves with status n. Runtime.halt(n) skips them completely.
  • No hook runs on SIGKILL or a kernel OOM-kill, so never make correctness depend on one. Flush as you go instead.

Example

public class Main {

    public static void main(String[] args) {
        Runtime.getRuntime().addShutdownHook(new Thread(() ->
                System.out.println("3. shutdown hook: flush logs, close the pool, then exit 0")));

        Thread poller = new Thread(() -> {
            while (true) {
                try { Thread.sleep(20); } catch (InterruptedException e) { return; }
            }
        }, "poller");
        poller.setDaemon(true);                  // a daemon never holds the JVM open
        poller.start();

        Thread worker = new Thread(() -> {
            try { Thread.sleep(300); } catch (InterruptedException e) { return; }
            System.out.println("2. worker finished - that was the last non-daemon thread");
        }, "worker");
        worker.start();                          // not a daemon, so the JVM must wait for it

        System.out.println("1. main() is returning now, and the process is still alive");
        System.out.println("   poller (daemon)  alive = " + poller.isAlive() + ", counts towards exit = no");
        System.out.println("   worker (normal)  alive = " + worker.isAlive() + ", counts towards exit = yes");
        System.out.println();
        System.out.println("Nothing below this line runs on the main thread.");
    }
}

The process ends with the last non-daemon thread, not with main().

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the JVM course, and every lesson in it is listed on the JVM contents page.