In Java, using throw/catch as a part of logic when there's not actually an error is generally a bad idea (in part) because throwing and catching an exception is expensive, and doing it many times in a loop is usually far slower than other control structures which don't involve throwing exceptions.
My question is, is the cost incurred in the throw/catch itself, or when creating the Exception object (since it gets a lot of runtime information including the execution stack)?
In other words, if I do
Exception e = new Exception();
but don't throw it, is that most of the cost of throwing, or is the throw + catch handling what's costly?
I'm not asking whether putting code in a try/catch block adds to the cost of executing that code, I'm asking whether catching the Exception is the expensive part, or creating (calling the constructor for) the Exception is the expensive part.
Another way of asking this is, if I made one instance of Exception and threw and caught it over and over, would that be significantly faster than creating a new Exception every time I throw?
Best Solution
Creating an exception object is not more expensive than creating other regular objects. The main cost is hidden in native
fillInStackTrace
method which walks through the call stack and collects all required information to build a stack trace: classes, method names, line numbers etc.The myth about high exception costs comes from the fact that most of
Throwable
constructors implicitly callfillInStackTrace
. However, there is one constructor to create aThrowable
without a stack trace. It allows you to make throwables that are very fast to instantiate. Another way to create lightweight exceptions is to overridefillInStackTrace
.Now what about throwing an exception?
In fact, it depends on where a thrown exception is caught.
If it is caught in the same method (or, more precisely, in the same context, since the context can include several methods due to inlining), then
throw
is as fast and simple asgoto
(of course, after JIT compilation).However if a
catch
block is somewhere deeper in the stack, then JVM needs to unwind the stack frames, and this can take significantly longer. It takes even longer, if there aresynchronized
blocks or methods involved, because unwinding implies releasing of monitors owned by removed stack frames.I could confirm the above statements by proper benchmarks, but fortunately I don't need to do this, since all the aspects are already perfectly covered in the post of HotSpot's performance engineer Alexey Shipilev: The Exceptional Performance of Lil' Exception.