Design of the Shutdown Hooks API

The following Q&A addresses some of the design issues of the Shutdown Hooks API.

Why don't you provide information as to why the VM is shutting down?

On some platforms a native process can't distinguish a shutdown due to exit from a shutdown due to termination. Other platforms provide much richer capabilities, in some cases including notification of system suspension and restart or of imminent power failure. In short, it's impossible to generalize such information in a portable way.

Will shutdown hooks be run if the VM crashes?

If the VM crashes due to an error in native code then no guarantee can be made about whether or not the hooks will be run.

Why are shutdown hooks run concurrently? Wouldn't it make more sense to run them in reverse order of registration?

Invoking shutdown hooks in their reverse order of registration is certainly intuitive, and is in fact how the C runtime library's atexit procedure works. This technique really only makes sense, however, in a single-threaded system. In a multi-threaded system such as Java platform the order in which hooks are registered is in general undetermined and therefore implies nothing about which hooks ought to be run before which other hooks. Invoking hooks in any particular sequential order also increases the possibility of deadlocks. Note that if a particular subsystem needs to invoke shutdown actions in a particular order then it is free to synchronize them internally.

Why are hooks just threads, and unstarted ones at that? Wouldn't it be simpler to use Runnable objects, or Beans-style event and listener patterns?

The approach taken here has two advantages over the more obvious, and more frequently suggested, callback-oriented designs based upon Runnable objects or Beans-style event listeners.

First, it gives the user complete control over the thread upon which a shutdown action is executed. The thread can be created in the proper thread group, given the correct priority, context, and privileges, and so forth.

Second, it simplifies both the specification and the implementation by isolating the VM from the hooks themselves. If shutdown actions were executed as callbacks then a robust implementation would wind up having to create a separate thread for each hook anyway in order for them to run concurrently. The specification would also have to include explicit language about how the threads that execute the callbacks are created.

Aren't threads pretty expensive things to keep around, especially if they won't be started until the VM shuts down?

Most implementations of the Java platform don't actually allocate resources to a thread until it's started, so maintaining a set of unstarted threads is actually very cheap. If you look at the internals of java.lang.Thread you can see that its various constructors just do security checks and initialize private fields. The native start() method does the real work of allocating a thread stack, etc., to get things going.

What about Personal and Embedded Java? Won't starting threads during shutdown be too expensive on those platforms?

This API may not be suitable for the smaller Java platforms. Threads in the Java 2 Platform carry more information than threads in JDK 1.1 and p/eJava. A thread has a class loader, it may have some inherited thread-local variables, and, in the case of GUI apps, it may be associated with a specific application context. Threads will come to carry even more information as the platform evolves; for example, the security team is planning to introduce a notion of per-thread user identity in their upcoming authentication framework.

Because of all this contextual information, shutdown hooks would be harder to write and maintain if they were just Runnable objects or Beans-style event listeners. Suppose that a Runnable shutdown hook, or an equivalent event listener, needed a specific bit of thread-contextual information in order to carry out its operations. Such information could be saved in some shared location before the hook is registered. While this is merely awkward, suppose further that threads acquire some new type of contextual information in a future release. If an operation invoked by the hook also evolves to need that information then the code that registers the hook would have to be amended to save that information as well. Making hooks be threads instead of Runnables or event listeners insulates them from this sort of future change.

Okay, but won't I have to write a lot of code just to register a simple shutdown hook?

No. Simple shutdown hooks can often be written as anonymous inner classes, as in this example:
Runtime.getRuntime().addShutdownHook(new Thread() {
    public void run() { database.close(); }
This idiom is fine as long as you'll never need to cancel the hook, in which case you'd need to save a reference to the hook when you create it.

What about security? Can an untrusted applet register a shutdown hook?

If there is a security manager installed then the addShutdownHook and removeShutdownHook methods check that the caller's security context permits RuntimePermission("shutdownHooks"). An untrusted applet will not have this permission, and will therefore not be able to register or de-register a shutdown hook.

What happens if a shutdown hook throws an exception and the exception is not caught?

Uncaught exceptions are handled in shutdown hooks just as in any other thread, by invoking the uncaughtException method of the thread's ThreadGroup object. The default implementation of this method prints the exception's stack trace to System.err and terminates the thread. Note that uncaught exceptions do not cause the VM to exit; this happens only when all non-daemon threads have finished or when the Runtime.exit method is invoked.

Why did you add the Runtime.halt method? Isn't it pretty dangerous?

The halt method is certainly powerful, and it should be used with the utmost caution. It's provided so that applications can insulate themselves from shutdown hooks that deadlock or run for inordinate amounts of time. It also allows applications to force a quick exit in situations where that is necessary.

What happens if finalization-on-exit is enabled? Will finalizers be run before, during, or after shutdown hooks?

Finalization-on-exit processing is done after all shutdown hooks have finished. Otherwise a hook may fail if some live objects are finalized prematurely.

Copyright © 1993, 2024, Oracle and/or its affiliates. All rights reserved.