How Java shuts down
Oct. 27th, 2006 07:37 pm![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
So one thing I discovered as I went back over my old posts is that I used to do some serious computer geeking in my posts. I mean, seriously.
And I haven't done any computer geeking on livejournal in a while, so:
Okay, the first of all, here's what Sun's javadoc says on the subject. Those few paragraphs are the only official documentation I was able to find, and while they may in fact tell the whole story do so very tersely.
So first off, what is system shutdown, and when does it happen? Well, the shutdown sequence is entered when the last non-daemon thread (see Thread#setDaemon) exits (either by throwing an exception or by returning from the run method) or when someone decides to call System.exit. (or Runtime.getRuntime().exit, which is the same thing) Nothing too outlandish there.
Oh wait, I lied. The shutdown sequence is also entered when the jvm receives a signal to shut down from the operating system (e.g. via "kill" in Unix, or pressing Ctrl-C in a cmd window on windows) that it deems means that it should shut down. So that the jvm doesn't miss any of these signals from the operating system, on unix it will helpfully re-activate the HUP signal handler
so that your java program can crash when you log out even if you started it with the standard unix incantation for running something in the background:
nohup java -cp /where/ever com.mycompany.ReallyImportantBackgroundJob &
(Yes, that's right: java laughs at nohup)
So okay, what does the shutdown sequence do? Well, first a few words about what it does not do:
Clearly, then, reliable java programming depends on installing shutdown hooks at appropriate places to call the same custom cleanup routines that you normally call from your finally blocks in case of error. No problem; just package that cleanup stuff up into a separate method and call it from your shutdown hook. But wait, there's more!
See, a common problem java programmers encounter is that they'll end up doing multi-threaded programming without really meaning to; this is especially true if you use a library such as ActiveMQ, which does nice things for you like send out messages to other programs in the background so that you don't have to worry about that. I'm told that java gui programmers have to deal with multiple background threads all the time too. Anyway, these threads aren't always daemon threads, and so a common practice is to call System.exit as the last thing your program does, usually in that finally block that the last paragraph just convinced you to move into a separate routine.
Only, because System.exit is never supposed to return, the bright people at Sun decided that if some thread calls System.exit while the shutdown process is already running, the result should be that that thread hangs until the first thread to call System.exit takes the whole jvm down. Sounds sensible - providing uniform behavior regardless of who calls System.exit first - but this means that if you call System.exit from inside a shutdown hook, everything hangs. Other shutdown hooks get to run, but because your shutdown hook never completes, the jvm never exits. (unless one of the other shutdown hooks calls Runtime.halt) Other threads that were running when shutdown started continue to run. Nothing can kill your process at this point except for a kill -9 (or equivalent).
So, okay, you have to have shutdown hooks to do all that cleanup work, and you have to avoid calling System.exit from your shutdown hook. If your cleanup routine can be called either from a shutdown hook or not, you have to check whether the system has started shutting down before you call System.exit. There's no direct public API for this, but you can hack together a method based on what kind of exception is thrown by trying to add a null shutdown hook. But then you should be okay, right?
Well, did you remember all those other threads still doing what they were doing before? You'll probably have to stop them to have your cleanup routine work properly. This is possible, but sometimes difficult to make sure that you do it properly. You have to arrange for your threads to check regularly if they should quit because of one reason or another; you can't rely on being able to interrupt the thread and have it stop its IO or database calls. (This is what practice tells me; I know you should get an InterruptedIOException, but that's not what's seen when a Mysql db goes toes up - what's seen is an uninterruptable thread)
Now, I haven't really touched on finalizers and system shutdown, because I don't use them, but I'll just mention one other thing: if you call System.exit from inside a finalizer that's running because we're already far into the shutdown sequence, and the status is other than zero, the jvm halts immediately. Calling System.exit(1) from inside a finalizer is a seriously dangerous thing to do, and should only be done if you really want everything to come crashing down RIGHT NOW, even without running any other finalizers that may be waiting. It's a mess.
So... that's the story about java and System.exit. Aren't you glad you aren't trying to implement reliable bits of system software in java?
And I haven't done any computer geeking on livejournal in a while, so:
Okay, the first of all, here's what Sun's javadoc says on the subject. Those few paragraphs are the only official documentation I was able to find, and while they may in fact tell the whole story do so very tersely.
So first off, what is system shutdown, and when does it happen? Well, the shutdown sequence is entered when the last non-daemon thread (see Thread#setDaemon) exits (either by throwing an exception or by returning from the run method) or when someone decides to call System.exit. (or Runtime.getRuntime().exit, which is the same thing) Nothing too outlandish there.
Oh wait, I lied. The shutdown sequence is also entered when the jvm receives a signal to shut down from the operating system (e.g. via "kill" in Unix, or pressing Ctrl-C in a cmd window on windows) that it deems means that it should shut down. So that the jvm doesn't miss any of these signals from the operating system, on unix it will helpfully re-activate the HUP signal handler
so that your java program can crash when you log out even if you started it with the standard unix incantation for running something in the background:
nohup java -cp /where/ever com.mycompany.ReallyImportantBackgroundJob &
(Yes, that's right: java laughs at nohup)
So okay, what does the shutdown sequence do? Well, first a few words about what it does not do:
- It does not cause code in finally blocks to get executed
- It does not stop or pause any threads that might still be running, so those threads will continue doing whatever it is they do while shutdown is going on
Clearly, then, reliable java programming depends on installing shutdown hooks at appropriate places to call the same custom cleanup routines that you normally call from your finally blocks in case of error. No problem; just package that cleanup stuff up into a separate method and call it from your shutdown hook. But wait, there's more!
See, a common problem java programmers encounter is that they'll end up doing multi-threaded programming without really meaning to; this is especially true if you use a library such as ActiveMQ, which does nice things for you like send out messages to other programs in the background so that you don't have to worry about that. I'm told that java gui programmers have to deal with multiple background threads all the time too. Anyway, these threads aren't always daemon threads, and so a common practice is to call System.exit as the last thing your program does, usually in that finally block that the last paragraph just convinced you to move into a separate routine.
Only, because System.exit is never supposed to return, the bright people at Sun decided that if some thread calls System.exit while the shutdown process is already running, the result should be that that thread hangs until the first thread to call System.exit takes the whole jvm down. Sounds sensible - providing uniform behavior regardless of who calls System.exit first - but this means that if you call System.exit from inside a shutdown hook, everything hangs. Other shutdown hooks get to run, but because your shutdown hook never completes, the jvm never exits. (unless one of the other shutdown hooks calls Runtime.halt) Other threads that were running when shutdown started continue to run. Nothing can kill your process at this point except for a kill -9 (or equivalent).
So, okay, you have to have shutdown hooks to do all that cleanup work, and you have to avoid calling System.exit from your shutdown hook. If your cleanup routine can be called either from a shutdown hook or not, you have to check whether the system has started shutting down before you call System.exit. There's no direct public API for this, but you can hack together a method based on what kind of exception is thrown by trying to add a null shutdown hook. But then you should be okay, right?
Well, did you remember all those other threads still doing what they were doing before? You'll probably have to stop them to have your cleanup routine work properly. This is possible, but sometimes difficult to make sure that you do it properly. You have to arrange for your threads to check regularly if they should quit because of one reason or another; you can't rely on being able to interrupt the thread and have it stop its IO or database calls. (This is what practice tells me; I know you should get an InterruptedIOException, but that's not what's seen when a Mysql db goes toes up - what's seen is an uninterruptable thread)
Now, I haven't really touched on finalizers and system shutdown, because I don't use them, but I'll just mention one other thing: if you call System.exit from inside a finalizer that's running because we're already far into the shutdown sequence, and the status is other than zero, the jvm halts immediately. Calling System.exit(1) from inside a finalizer is a seriously dangerous thing to do, and should only be done if you really want everything to come crashing down RIGHT NOW, even without running any other finalizers that may be waiting. It's a mess.
So... that's the story about java and System.exit. Aren't you glad you aren't trying to implement reliable bits of system software in java?