Wednesday, July 21, 2010

How much damage can a Java Error do?

In Java there is a clear distinction between recoverable Exceptions and fatal Errors. It's common for applications to have a catch-all block so that they can continue running even if unexpected problems occur:
while (true) {
 try {
  // execute application logic
 } catch (Throwable t) {
  // log exception and/or display to the user
 }
}
This will catch both Exceptions and Errors. Although it's fine to catch an Exception and have the application carry on, this is typically not a good idea when an Error occurs!

I recently saw a bug in an application that caused a StackOverflowError. However, because of a catch-all block the application tried to recover from the problem and carry on. As it turns out this caused all kinds of instability and seemingly unrelated problems: connection leaks, ThreadLocal corruption and other such joys.

If you think about it this makes sense. A StackOverflowError indicates that the JVM ran out of stack space to properly invoke methods. Of course this has implications for things like finally blocks, and certainly for method invocations in those finally blocks! If you can no longer rely on the proper execution of your finally blocks you end up in a world of pain with things like messed up ThreadLocals. There are similar considerations for OutOfMemoryError and certainly for the other VirtualMachineErrors.

In summary: Errors can do a lot of damage! Don't keep the application running if you encounter an Error. Instead, try to log the problem and die. Only recover in case of Exceptions.
while (true) {
 try {
  // execute application logic
 } catch (Exception e)
  // log error and/or display to the user
  // => recover
 } catch (Error e) {
  // log error and/or display to the user
  // => exit JVM
 }
}