Can one trust an exception in Java?

by Martin Monperrus and Benoit Cornu
By working on Java exceptions, we recently discovered that exceptions are not as trustful as we would have expected.

Never Trust the Stack Trace

It is widely known that an exception embeds a stack trace. They are often visible, even for end users, and developers often use ex.printStackTrace() to print it on the standard output. The stack trace information can also be obtained with ex.getStackTrace().

The stacktrace should never be trust to take some decisions or reason on the exception.

First, the stack trace can be set using setStackTrace(StackTraceElement[] stackTrace). This actually happens in some libraries.

Second, the stack trace is set at instantiation time. If a developer puts an exception in a variable and later throws it (when the stack shape has completely changed), the exception stack trace does not anymore correlate with the current execution stack! There is then a fundamental inconsistency.

Finally, the JVM itself can eat the stacktrace, as shown by Nikita Salnikov-Tarnovski in On a quest for missing stacktraces.

Note that to know the current stack at catching time, a stack trace is always available with Thread.currentThread().getStackTrace();, so one can write something as:
catch (Exception ex) {
  StacktraceElement[] elems = Thread.currentThread().getStackTrace();
  // do something with elems
  ...
}

Never assume that an exception is a fresh object


When one catches an exception, in 99% of the cases, it has been recently created. However, this may not necessarily be the case. The language and the platform allows rethrowing an exception several times (as well as storing exception objects for future throwing).

The well known case of the exception creation site is not the throw site is the classical rethrow:
catch (Exception ex) {
  // do something
  ...

  throw ex; // the exception is thrown for the n-th time
}
Exception as constants
Since creating a new exception object is costly with respect to performance, one can imagine:
class Constants {
  public final static ex = new Exception(); 
}

// somewhere in the code
throw Constants.ex; // the exception may be thrown n times
This performance pattern is indeed used, for instance in generated parsers with JavaCC (e.g. "org.gjt.sp.jedit.bsh.Parser"):
final private LookaheadSuccess jj_ls = new LookaheadSuccess(); // is an exception


Ping pong

A extreme artificial case is the following, where we play ping-pong with an exception:
Exception ex = new Exception();
for (int i=0; i<10;i++) {
  try {
    // do something 
    ....
    throw ex;// ping
  } catch(Exception e) {    
    // caught for pong
    // ex == e 
  }
}
Tagged as: