JVM Startup and Shutdown: Bootstrap, Initialization Hooks, and Shutdown Sequence

Understanding JVM lifecycle: bootstrap process, initialization hooks, shutdown sequence, and how to implement lifecycle callbacks for graceful shutdown.

published: reading time: 24 min read author: GeekWorkBench

JVM Startup and Shutdown: Bootstrap, Initialization Hooks, and Shutdown Sequence

The JVM goes through a well-defined lifecycle from startup to shutdown. Understanding this lifecycle helps you debug startup issues, implement graceful shutdown, and use initialization hooks for setup tasks that must run before your application starts.

This post covers the JVM bootstrap process, how classes are loaded during startup, initialization hooks that run on startup and shutdown, and the shutdown sequence itself.

Introduction

The JVM follows a rigorous lifecycle from the moment you invoke the java command until the process terminates. The bootstrap phase loads core JDK classes, initializes the garbage collector and JIT compiler, registers shutdown hooks, and eventually transfers control to your application main method. At the other end, the shutdown sequence runs registered hooks concurrently, finalizes remaining objects, and calls _exit() to hand control back to the operating system. Most developers never need to think about this lifecycle — it works correctly by default. But when startup problems surface in containerized environments, when graceful shutdown does not behave as expected, or when initialization hooks need to warm caches before the application begins, understanding the JVM lifecycle becomes essential.

For practitioners, JVM startup and shutdown matters in three scenarios. First, when debugging class loading order problems or circular dependency failures during bootstrap, where the sequence of events determines whether initialization succeeds or throws ExceptionInInitializerError. Second, when implementing graceful shutdown in services where containers send SIGTERM and expect the application to clean up and exit within a timeout — shutdown hooks that block indefinitely or get skipped entirely cause crashes, failed liveness probes, or resource leaks. Third, when working with initialization hooks for pre-warming caches or setting up profiling agents like JVMTI tools that must run before application code executes. Understanding when shutdown hooks run, what can cause them to be skipped, and how signal handling interacts with the lifecycle prevents production incidents that are difficult to debug after the fact.

This post walks through the bootstrap sequence step by step, explains how initialization and shutdown hooks work and what they can and cannot do, covers the five phases of the JVM shutdown sequence, describes signal handling on Unix-like systems, and provides production failure scenarios and implementation patterns for graceful shutdown. By the end, you will understand exactly what happens between java -jar and your application starting, and between calling System.exit() and the process actually terminating.

When NOT to Use

JVM startup and shutdown details matter when debugging startup failures, implementing graceful shutdown, or integrating with container orchestrators. A simple application that exits cleanly does not need you to understand bootstrap sequencing or hook registration. The JVM handles these correctly for most cases without intervention. Only dig into lifecycle internals when you hit class loading order problems, hook timing issues, or signal handling bugs.

Do not use initialization hooks for setup that belongs in static initializers or your main method. Hooks are JVM-internal and harder to debug. Do not use shutdown hooks for cleanup that must run every time, either. Shutdown hooks can be skipped entirely if the process gets killed with SIGKILL, the JVM crashes, or someone calls halt(). Put critical cleanup synchronously during normal operation, not asynchronously at shutdown where it may never execute.

Modifying the bootstrap classpath is rarely necessary and frequently breaks JVM assumptions. If you are adding classes there to work around classloader issues, fix the classloader problem directly. Shutdown hook timing is not guaranteed relative to specific application threads; do not use hooks for operations that need ordering guarantees without explicit synchronization. Runtime Data Areas and Execution Engine cover what gets initialized during bootstrap.

JVM Bootstrap Process

When you run java -jar myapp.jar, the JVM begins a complex bootstrap sequence before your main method executes.

Bootstrap Sequence

sequenceDiagram
    participant OS as Operating System
    participant JVM as JVM Process
    participant Boot as Bootstrap Loader
    participant Main as Main Thread

    OS->>JVM: Create process, load JVM library
    JVM->>Boot: Initialize Bootstrap ClassLoader
    Boot->>Boot: Load core JDK classes
    JVM->>JVM: Create runtime system (Thread, GC, etc.)
    JVM->>JVM: Register shutdown hooks
    JVM->>Main: Load application main class
    Main->>Main: Execute main() method
    Note over Main: Application runs
    Main-->>JVM: main() returns or System.exit()
    JVM->>JVM: Run shutdown hooks
    JVM->>JVM: Finalize finalize() methods
    JVM-->>OS: Process exits

Step-by-Step Bootstrap

  1. Operating System creates the JVM process. This involves loading the JVM dynamic library (libjvm.so on Linux, jvm.dll on Windows) into memory and initializing native structures.

  2. Bootstrap ClassLoader loads. The native bootstrap loader loads core Java classes from rt.jar (Java 8 and earlier) or from the modules image (Java 9+). These include java.lang.Object, java.lang.Class, java.lang.String, and all other fundamental classes.

  3. JVM runtime system initializes. This includes:

    • Creating the GC system
    • Initializing the interpreter
    • Setting up the JIT compiler infrastructure
    • Creating the main thread and thread group
    • Initializing the class loader subsystem
  4. Command line arguments are processed. The JVM parses -X, -XX, and system properties. These affect GC algorithms, heap sizing, JIT behavior, and more.

  5. Shutdown hook registration. The JVM registers internal shutdown hooks (for flight recorder, etc.) and makes the application hooks registered via Runtime.addShutdownHook() available.

  6. Application main class loads. The class specified on the command line is loaded. Static initializers run.

  7. Main method executes. Control transfers to your application code.

Initialization Hooks

The JVM supports initialization hooks that run during startup. These are not the same as shutdown hooks, though the API is similar.

Using Initialization Hooks

Initialization hooks are threads that start during JVM initialization, before the application starts. They are useful for:

  • Pre-warming caches
  • Initializing native libraries
  • Verifying external dependencies
  • Setting up JVMTI (JVM Tool Interface) agents
public class InitializationHookExample {
    static {
        System.out.println("Static initializer runs during class loading");
    }

    public static void main(String[] args) {
        System.out.println("Main method runs after initialization hooks");
    }
}

Pre-Initialization with -XX:CompileCommand

For extremely early initialization, you can use the CompileCommand to force JIT compilation before first use:

java -XX:CompileCommand=option,MyClass::hotMethod,BiasedLocking -jar myapp.jar

This compiles MyClass.hotMethod() with biased locking enabled before the application starts.

Shutdown Hooks

Shutdown hooks are threads that the JVM runs during the shutdown sequence. They are guaranteed to run even if the JVM terminates abruptly.

When Shutdown Hooks Run

Shutdown hooks run when:

  • All non-daemon threads have finished
  • System.exit() is called (from application code or another shutdown hook)
  • The JVM receives a termination signal (SIGTERM, Ctrl+C) and has not disabled shutdown hooks
  • Runtime.halt() is called (forceful termination without running hooks, but shutdown hooks ARE run unless forced with _exit)

Implementing Shutdown Hooks

import java.util.concurrent.*

public class ShutdownHookExample {
    private static final ExecutorService executor =
        Executors.newFixedThreadPool(4);

    public static void main(String[] args) {
        // Register shutdown hook
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            System.out.println("Shutdown hook running");
            shutdownExecutor();
        }, "shutdown-hook"));

        // Application code
        System.out.println("Application running");
    }

    private static void shutdownExecutor() {
        executor.shutdown();
        try {
            if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}

Shutdown Hook Rules

  1. Do not reference daemon threads. Daemon threads may terminate before the shutdown hook completes.

  2. Do not depend on other application threads. By the time shutdown hooks run, all non-daemon threads have terminated.

  3. Keep shutdown hooks fast. The JVM waits for all shutdown hooks to complete before exiting. Slow shutdown hooks delay termination.

  4. Do not use System.exit() in a shutdown hook. This causes a deadlock as the JVM waits for the current shutdown hook to complete before starting the new exit.

  5. Handle exceptions. Uncaught exceptions in shutdown hooks terminate the hook but do not stop other shutdown hooks.

  6. Do not assume hooks run in a particular order. Multiple hooks may run concurrently.

Shutdown Sequence

The JVM shutdown sequence follows a defined order:

Phase 1: Shutdown Request

Shutdown is requested through one of:

  • All non-daemon threads terminating normally
  • System.exit() call
  • Termination signal (SIGTERM, SIGINT, Ctrl+C)
  • Runtime.halt() call (immediate termination)

Phase 2: Running Shutdown Hooks

The JVM spawns new threads to run registered shutdown hooks. These hooks run concurrently and independently.

The JVM waits for all shutdown hooks to complete. There is no timeout; the JVM waits indefinitely unless:

  • Runtime.halt() is called from a hook
  • The JVM crashes
  • The operating system terminates the process forcefully

Phase 3: Finalization

After all shutdown hooks complete, the JVM:

  • Runs all uninvoked finalizers (if finalization is not disabled)
  • Performs the last GC if needed

Note: As of Java 9+, Object.finalize() is deprecated and should not be used.

Phase 4: Process Exit

The JVM calls _exit() to terminate the process. At this point, all shutdown hooks have completed and finalization is done.

Signal Handling

On Unix-like systems, the JVM handles signals for:

  • SIGTERM: Graceful shutdown request. Runs shutdown hooks.
  • SIGINT: Interrupt from Ctrl+C. Runs shutdown hooks.
  • SIGQUIT: Thread dump request (jstack). Does not trigger shutdown.
  • SIGABRT: JVM crash. Generates hs_err crash log.
  • SIGHUP: Often used for configuration reload. Not handled by JVM by default.
# Send SIGTERM for graceful shutdown
kill -TERM <pid>

# Send SIGINT (Ctrl+C equivalent)
kill -INT <pid>

# Request thread dump without killing
kill -QUIT <pid>

Custom Signal Handling

JVM TI (Tool Interface) agents can register for signal handling:

// JVM TI agent registration
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {
    jvmtiEnv *jvmti;
    (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1_0);

    // Set notification for breakpoint, single step, etc.
    jvmtiError err = (*jvmti)->SetEventNotificationMode(
        jvmti, JVMTI_ENABLE,
        JVMTI_EVENT_BREAKPOINT, NULL);

    return JNI_OK;
}

Production Failure Scenarios

ScenarioRoot CauseSymptomsResolution
Shutdown hooks do not runJVM killed with SIGKILLCleanup code skippedAvoid SIGKILL, use SIGTERM
Deadlock during shutdownHook calls System.exit()JVM hangs on shutdownRemove System.exit() from hooks
Slow shutdown hooksBlocking I/O in hookDelayed shutdownMake hooks non-blocking
Shutdown hook exceptionUncaught exception in hookHook terminates, others continueAlways catch exceptions in hooks
Finalizers run unexpectedlyObject became unreachableDelayed cleanup, resource leaksAvoid finalize(), use Cleaner
Signal handling blockedLong shutdown hook or GCSIGTERM ignoredKeep hooks fast, tune GC

Trade-off Analysis

Trade-offConsiderations
Fast Shutdown vs. Clean ShutdownForcing immediate termination risks data loss. Waiting for cleanup takes time.
Daemon vs. Non-Daemon ThreadsDaemon threads die with the JVM, potentially leaving cleanup incomplete. Non-daemon threads keep the JVM alive.
Synchronous vs. Asynchronous CleanupSynchronous cleanup is simpler but can block. Asynchronous cleanup is non-blocking but more complex.
Signal Handling vs. Shutdown HooksSignals provide immediate response. Shutdown hooks run after the decision to terminate is made.

Failure Scenarios Deep Dive

JVM Fails to Start Due to Class Loading Circular Dependency

A static initializer in class A triggers loading of class B, whose static initializer triggers loading of class C, which eventually triggers loading back to class A before A’s static initialization completes. The JVM detects this circularity and throws a StackOverflowError or ExceptionInInitializerError. This happens when static fields have complex initialization expressions that indirectly reference the same class. The fix is to break the circular dependency: move the initialization to a separate method called explicitly after construction, or refactor to remove the circular reference.

Shutdown Hook Never Runs Because All Non-Daemon Threads Never Terminate

If your application uses non-daemon threads that never terminate (for example, a thread pool with blocking tasks, or a long-running background task), the JVM never proceeds to the shutdown hook phase. The application just sits running even after main() returns. Ensure your application explicitly calls System.exit() or sets daemon threads appropriately if you rely on shutdown hooks for cleanup.

Flight Recorder Crash Dump Generated on JVM Crash but Process Exits Before You Can Read It

When the JVM crashes (SIGABRT), it generates an hs_err log and may start a Java Flight Recorder dump. If the disk fills up during the dump or the process is killed immediately after generating the crash log, the dump may be incomplete or truncated. Configure -XX:ErrorFile= to write the hs_err log to a partition with sufficient space, and ensure monitoring detects disk space issues before they cause dump truncation.

Implementation Patterns

// Graceful shutdown with proper resource cleanup
public class GracefulShutdown {
    private volatile boolean shuttingDown = false;

    public static void main(String[] args) {
        GracefulShutdown app = new GracefulShutdown();
        app.registerShutdownHook();
        app.run();
    }

    private void registerShutdownHook() {
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            shuttingDown = true;
            cleanup();
        }));
    }

    private void run() {
        while (!shuttingDown) {
            // Application loop
            processNextTask();
        }
    }

    private void cleanup() {
        System.out.println("Cleaning up resources");
        // Close files, connections, etc.
    }

    private void processNextTask() {
        // Task processing
    }
}
// Distributed tracing context propagation during shutdown
public class TracingShutdown {
    public static void main(String[] args) {
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            // Export tracing data before exit
            Tracing.flush();
        }));
    }
}
// Health check endpoint for graceful load balancer removal
public class HealthCheckServer {
    private volatile boolean healthy = true;

    public void start() {
        // Register shutdown hook to set unhealthy
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            healthy = false;
            // Give load balancer time to remove from pool
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }));

        // Start HTTP server on /health
    }

    public boolean isHealthy() {
        return healthy;
    }
}

Observability Checklist

  • Monitor JVM uptime to detect unexpected restarts
  • Log shutdown hook execution times to detect slow shutdowns
  • Track graceful vs. forced shutdown ratio
  • Monitor for SIGKILL (immediate termination) occurrences
  • Ensure heap dumps on OOM are written before shutdown
  • Verify shutdown hooks complete before process exit
  • Track finalizer queue depth if using deprecated finalize()
  • Monitor for native memory leaks that prevent clean shutdown
  • Loguncaught exceptions in shutdown hooks

Security Notes

Shutdown hooks run with full JVM privileges. Malicious shutdown hooks can execute arbitrary code. Ensure shutdown hooks are registered only by trusted code.

External processes that send signals to the JVM should be authorized. Anyone who can send SIGTERM to the JVM process can trigger shutdown hooks.

Do not store sensitive data in shutdown hook threads without synchronization. By the time shutdown hooks run, other application threads have terminated.

Common Pitfalls / Anti-Patterns

Registering shutdown hooks from library code. Libraries should never register shutdown hooks unless absolutely necessary. Library shutdown hooks can conflict with application shutdown hooks and cause unpredictable behavior.

Assuming shutdown is always graceful. SIGKILL (kill -9) terminates the JVM immediately without running shutdown hooks. Service managers like systemd send SIGTERM first, but processes can be killed with SIGKILL if they do not terminate quickly enough.

Using shutdown hooks for critical cleanup. Shutdown hooks are not guaranteed to run in all cases (JVM crash, SIGKILL, Runtime.halt()). Critical cleanup should happen synchronously before critical operations, not asynchronously at shutdown.

Blocking in shutdown hooks. If a shutdown hook blocks, the JVM waits indefinitely. Avoid any blocking operations: network calls, file I/O beyond what’s absolutely necessary, or acquiring locks that other threads might hold.

Forgetting that daemon threads do not trigger shutdown hooks to wait. If your application relies on non-daemon threads to prevent premature shutdown, remember that shutdown hooks run after those threads terminate. If those threads are holding resources needed by hooks, you have a problem.

Quick Recap Checklist

  • JVM bootstrap loads core classes, initializes GC and JIT, then runs main()
  • Shutdown hooks run when the JVM receives termination signal or System.exit()
  • All non-daemon threads must terminate before shutdown hooks run
  • Shutdown hooks run concurrently and independently
  • Do not call System.exit() from within a shutdown hook
  • Keep shutdown hooks fast and handle all exceptions
  • Runtime.halt() terminates immediately without running hooks
  • SIGKILL (-9) cannot be intercepted and bypasses shutdown hooks
  • Finalizers run after shutdown hooks complete
  • Daemon threads may terminate during shutdown without cleanup
  • Shutdown hooks are not guaranteed to run in all termination scenarios

Interview Questions

1. What is the order of events during JVM shutdown?

When shutdown is requested (via System.exit(), signal, or all non-daemon threads terminating), the JVM first determines if shutdown hooks are registered. If so, it spawns threads to run all registered shutdown hooks concurrently and waits for them to complete. After all shutdown hooks finish, the JVM runs any uninvoked finalizers (deprecated in Java 9+). Finally, the JVM calls _exit() to terminate the process. The key guarantee is that shutdown hooks are run even if the JVM terminates due to an error in the application code, but they are NOT run if the process is killed with SIGkill.

2. What happens if a shutdown hook calls System.exit()?

If a shutdown hook calls System.exit(), it causes a deadlock. The JVM has already started shutdown hook execution and is waiting for all hooks to complete before proceeding with the rest of the shutdown sequence. When System.exit() is called from a hook, the JVM attempts to start a new shutdown sequence, which cannot proceed because the current shutdown is still in progress. The JVM will hang indefinitely. The solution is to never call System.exit() from a shutdown hook. If you need to terminate from a hook, use Runtime.getRuntime().halt() instead.

3. How do you implement graceful shutdown of an ExecutorService?

Inside a shutdown hook, call executor.shutdown() to stop accepting new tasks. Then call executor.awaitTermination() with a timeout to wait for running tasks to complete. If the timeout expires, call executor.shutdownNow() to cancel running tasks and remove queued tasks from the queue. Always handle InterruptedException: if interrupted during awaitTermination, decide whether to continue waiting or cancel immediately. The shutdown hook should return (not block indefinitely) to allow the JVM to continue the shutdown sequence.

4. What signals does the JVM handle and how?

The JVM handles several signals: SIGTERM requests graceful shutdown, running shutdown hooks before terminating. SIGINT (Ctrl+C) also requests graceful shutdown. SIGQUIT requests a thread dump without terminating, useful for diagnostics. SIGABRT indicates the JVM crashed and triggers hs_err crash log generation. SIGKILL cannot be intercepted and terminates immediately. Signal handling is platform-dependent; Windows does not have signal equivalents for everything.

5. Why should you avoid using Object.finalize() and what should you use instead?

Object.finalize() is deprecated since Java 9 for several reasons: there is no guarantee when or if it runs, exceptions in finalizers can leave objects in a corrupt state, finalizers can resurrect objects (making them reachable again), and they impose performance overhead on every object allocation. Instead, use java.lang.ref.Cleaner for resources that must be cleaned up but should not block GC. For explicit resource cleanup (files, connections), use try-with-resources which guarantees cleanup at a specific point in code.

6. What happens during the JVM bootstrap phase before the main method executes?

The bootstrap phase involves: First, the OS loads the JVM shared library and creates the JVM process. Second, the bootstrap classloader loads core JDK classes from rt.jar (Java 8) or the modules image (Java 9+), including java.lang.Object, java.lang.Class, and all fundamental classes. Third, the JVM initializes internal subsystems: the garbage collector, interpreter, JIT compiler infrastructure, main thread and thread group, and classloader subsystem. Fourth, command line arguments are parsed. Fifth, shutdown hooks are registered. Sixth, the application main class is loaded and its static initializers run. Finally, the main method executes.

7. What is the difference between Runtime.halt() and System.exit() in shutdown hooks?

System.exit() initiates a complete JVM shutdown sequence, which includes running all registered shutdown hooks. If called from a shutdown hook, it attempts to start a nested shutdown sequence, causing a deadlock because the JVM is already waiting for all hooks to complete. Runtime.halt() terminates the JVM immediately without running any shutdown hooks. The only way to halt without running hooks is to call halt from outside the shutdown sequence entirely. Use halt only when you need to force termination and are certain no cleanup is needed or possible.

8. Why might a shutdown hook run multiple times or not at all?

Shutdown hooks run at most once per JVM instance because the JVM marks hooks as complete after execution. However, if the same hook thread is registered multiple times (for example, the same Runnable instance passed to addShutdownHook twice), the JVM treats them as separate hook entries and runs the hook multiple times. A shutdown hook may not run if: the JVM crashes (SIGABRT does not guarantee hook completion), the process is killed with SIGKILL, Runtime.halt() is called, or the operating system terminates the process forcefully.

9. How does the module system affect class loading order during JVM startup?

In Java 9+, the module system defines a strict ordering for class loading during startup. The boot module (java.base) is loaded first by the bootstrap classloader. Other JDK modules (java.sql, java.xml, etc.) are loaded as needed based on the module graph. Application modules are loaded after the JDK modules, following the requires-dependencies declared in each module's module-info.java. This means a module that requires another cannot be loaded before its dependency. Circular dependencies between modules are disallowed at runtime.

10. What is the -XX:+HandlePromotionFailure flag and why was it significant for GC?

The -XX:+HandlePromotionFailure flag controlled how the young generation collector handled promotion failures during garbage collection. A promotion failure occurred when the JVM tried to promote an object from young to old generation but found insufficient contiguous space. When enabled, the JVM would perform a "defensive" collection, copying fewer objects to old generation and keeping more in survivor spaces. This flag was significant for G1 and CMS collectors. Starting from Java 7u4, the HotSpot JVM made this behavior the default (always enabled), and the flag was eventually removed in newer Java versions.

11. What is the difference between initialization hooks and shutdown hooks?

Initialization hooks are threads that start during JVM initialization, before the application main method runs. They are useful for pre-warming caches, initializing native libraries, verifying external dependencies, or setting up JVMTI agents. Shutdown hooks are threads registered via Runtime.addShutdownHook() that run during JVM shutdown, after application threads have terminated but before process exit. They serve different lifecycle phases: startup vs. shutdown of the JVM process.

12. How does Flight Recorder integrate with JVM shutdown?

Java Flight Recorder (JFR) runs as a JVM-internal shutdown hook. When the JVM receives SIGABRT (crash) or normal shutdown, FSR dumps its recorded data to a file (`.jfr` file) before the process exits. This allows post-mortem analysis of what happened leading up to a crash. FSR data is written in a binary format that can be analyzed with JDK Mission Control. If the JVM crashes, the hs_err log and FSR dump provide complementary diagnostic information.

13. What are the five phases of JVM shutdown in order?

Phase 1: Shutdown requested (via System.exit(), signal, or all non-daemon threads ending). Phase 2: Running shutdown hooks—all registered hooks run concurrently; the JVM waits indefinitely for them to complete unless Runtime.halt() is called. Phase 3: Finalization—runs any uninvoked finalizers (deprecated in Java 9+). Phase 4: JVM termination—the JVM calls _exit() to terminate the process. Phase 5: OS-level process cleanup (kernel reclaims resources).

14. What happens when a static initializer causes an exception during class loading?

If a static initializer throws an exception that is not caught, the class is marked as having failed initialization. Subsequent attempts to use the class throw ExceptionInInitializerError wrapping the original exception. Other threads that were waiting for the class to initialize will also receive ExceptionInInitializerError. This prevents classes from being used in a partially-initialized state, which could cause hard-to-debug behavior. The original exception is preserved as the cause of ExceptionInInitializerError.

15. Why should shutdown hooks be fast and non-blocking?

The JVM waits for all shutdown hooks to complete before exiting. If any hook blocks indefinitely (waiting on network I/O, file I/O, or locks held by other threads), the JVM hangs on shutdown. Hooks that take too long delay process termination, which matters for container orchestrators and service managers expecting quick restarts. Best practice: perform cleanup synchronously during normal operation, not asynchronously at shutdown where hooks may never run (SIGKILL, crash).

16. What is the role of the VMThread during JVM shutdown?

The VMThread is the internal JVM thread responsible for executing VM operations, including shutdown sequence execution. When shutdown is triggered, the VMThread coordinates the shutdown hooks, finalization, and other VM-level cleanup tasks. It ensures these run in the proper order on dedicated hook threads. The VMThread is not directly accessible from Java code—it is internal to the JVM implementation.

17. How does JVM initialization interact with the Safety SE6?

During initialization, the JVM must ensure the core classes are properly loaded and linked before any user code runs. The bootstrap classloader loads java.lang.Object, java.lang.Class, and all fundamental types. Static fields get default values, and static initializers run in a specific order. The JVM verifies that core classes pass bytecode verification before executing any of their methods. This creates a trusted foundation on which all other Java code depends.

18. What happens if you register the same shutdown hook twice?

If the same Thread or Runnable instance is registered twice via addShutdownHook(), the JVM treats them as two separate hook entries. Both will run during shutdown, potentially causing duplicate cleanup operations. Design hooks to be idempotent if possible, or guard against double execution with a flag. The JVM does not deduplicate hook registrations.

19. How does -XX:+TraceClassInitialization help diagnose startup issues?

The -XX:+TraceClassInitialization flag prints a log line for each class as it is initialized, including the timestamp and any exception that occurred. This helps diagnose circular class initialization deadlocks or failures during startup. When classes fail to initialize, the trace shows which class's static initializer threw, allowing you to pinpoint the problem. Combine with GC logs for a complete picture of JVM startup behavior.

20. What is the relationship between daemon threads and shutdown hooks?

Daemon threads do not prevent the JVM from shutting down. When all non-daemon threads have terminated, the JVM proceeds to the shutdown hook phase regardless of daemon threads still running. However, daemon threads may be terminated at any point during shutdown hook execution, which can cause problems if hooks depend on daemon threads for cleanup. Do not rely on daemon threads being alive during shutdown hooks.

Further Reading

Conclusion

JVM startup involves loading core classes, initializing the runtime system (GC, JIT, threads), and executing your main method. Shutdown runs registered hooks concurrently before finalization and process exit. Implement graceful shutdown by registering hooks that clean up resources, never call System.exit() from a hook, and keep hooks fast and non-blocking. SIGKILL bypasses all shutdown handling.

Category

Related Posts

GraalVM Native Image

Understand ahead-of-time compilation with GraalVM Native Image and the Substrate VM runtime for near-instant startup and minimal memory footprint.

#graalvm #native-image #aot

Async-Profiler: Low-Overhead CPU and Memory Profiling

Learn async-profiler for low-overhead CPU and memory profiling in production. Generate flame graphs, analyze allocations, and diagnose JVM bottlenecks.

#jvm #async-profiler #cpu-profiling

Java Atomics and VarHandle: Low-Level Concurrency

Understanding Java atomic operations: AtomicInteger, AtomicReference, VarHandle, compareAndSet, atomics vs locks, and lock-free programming patterns.

#java #jvm #concurrency