Debugging Deadlocks – Print All Stack Traces
One of the hardest types of bugs to track down is a deadlock situation. They are very time dependant which makes them intermitten, specific to a particular computer and configuration and generally impossible to reproduce in a debugger. Fortunately, at least in Java, it’s fairly easy to spot most of the situations where a deadlock is possible:
- Calls to SwingUtilities.invokeAndWait
- Calls to Object.wait()
- synchronized blocks
There might be a few others depending on the libraries you’re using, but starting with those three, in that order, is very likely to lead you to at least one point in the deadlock. Just put an old fashioned System.err.println before and after each of those calls and you’ll quite quickly see where things are waiting forever.
Deadlocks have to have at least two threads involved. To find the other one, you need to print the stack traces of all the other threads immediately before you actually hit the deadlock. So once you’ve found one call that’s part of the deadlock, add a call to the method below just before it.
private static void printAllStackTraces() { Map liveThreads = Thread.getAllStackTraces(); for (Iterator i = liveThreads.keySet().iterator(); i.hasNext(); ) { Thread key = (Thread)i.next(); System.err.println("Thread " + key.getName()); StackTraceElement[] trace = (StackTraceElement[])liveThreads.get(key); for (int j = 0; j < trace.length; j++) { System.err.println("\tat " + trace[j]); } } }
You’ll get a whole bunch of threads and with a bit of digging around to see exactly what the code is doing in each thread you’ll probably find a pair of threads like below that are waiting on each other (package names removed to make things shorter):
Thread Thread-14 at Object.wait(Native Method) at java.lang.Object.wait(Object.java:474) at EventQueue.invokeAndWait(EventQueue.java:848) at SwingUtilities.invokeAndWait(SwingUtilities.java:1257) at CachingFileDownloader.run(CachingFileDownloader.java:204) at WorkerThread.run(WorkerThread.java:49) Thread AWT-EventQueue-2 at Object.wait(Native Method) at Object.wait(Object.java:474) at CachingFileDownloader.waitUntilDownloadComplete(CachingFileDownloader.java:290) <snip>
Yep, the swing thread is waiting on the download and the download thread is trying to do something on the swing thread via invokeAndWait. It’s actually trying to close the download progress dialog which can most likely just use an invokeLater, thus avoiding the deadlock.
This is also a really good example of why I put SwingUtilities.invokeAndWait at the top of my hit list. Also, the actual call to invokeAndWait that I put the debugging above, wasn’t really involved in the deadlock, it just happened to need to wait on the Swing thread as well and wound up waiting forever because the other two threads were deadlocked with each other.
As a side note, I think this is the first time since we started publishing the twice daily builds to my blog automatically that I’ve had a catastrophic failure where I couldn’t post at all. That’s not a bad run considering it’s been at least a couple of years living on the edge.
Sigh, and upon hitting save I find that the debugging code I added above can cause security exceptions when triggered as part of our form submission process in Safari (but not FireFox) on OS X. So if you originally saw this post with no content, that would be why…

November 24th, 2008 at 9:27 pm
kill -QUIT does a stack dump of a JVM; on JRockit it even lists which item every thread is waiting on, which is generally all you need to identify deadlocks
November 24th, 2008 at 9:30 pm
Oh yes, I meant to mention that but forgot, thanks for reminding me. It’s very useful but tends to be hit and miss for debugging applets – especially when you’ve deadlocked the UI thread which tends to crash the browser.
November 24th, 2008 at 9:41 pm
You should also look into jvisualvm of JDK 1.6.0_10 (formerly jconsole). It can do that with one click and even lets you browse the heap and do some basic profiling. Stuff that you needed a good IDE for before. Quite amazing.
November 24th, 2008 at 9:48 pm
Odi,
Interesting, I’ll have to keep that in mind when I next get a deadlock on Windows – this one was on my Mac so Java 6 wasn’t an option (Safari is 32bit, Java 6 on Mac is 64bit only so applets only use Java 5).
I wonder if jvisualvm would affect the timing and cause the issue to not happen though? I know it’s very low overhead so it’s got a pretty good chance, but I’m not sure if it’s completely zero overhead. Seems a lot more likely to work than a debugger though.
November 24th, 2008 at 11:26 pm
JConsole has been the best-kept java secret on windows for years, although the JDK5 version wasn’t quite as good :)
The latest iteration (it was still jconsole in the 6u10 betas, but I haven’t looked for it since the release) is pretty darn cool – it will give you a BLOCKED status on deadlocked threads and tell you the object hashcode it is blocked on, along with the thread ID that is holding it. Makes debugging deadlocks a breeze.
It’s actually useful even without deadlocks – I’ve used it many times to generate HPROF dumps of a running JVM for debugging leaks in SAP Memory Analyser.
November 25th, 2008 at 6:11 am
Or hit CTRL-Break on the console, but that can suck if you’ve got a big application with deep stack traces like we do. Alternatively, I seem to remember Eclipse having some fancy deadlock detection and monitor debugging stuff in their Java debugger too, although I’m not too sure how good they are for thread debugging, since I’ve only been forced to use it a couple of times.