Question: Is exception handling in Java actually slow?
Conventional wisdom, as well as a lot of Google results, says that exceptional logic shouldn't be used for normal program flow in Java. Two reasons are usually given,
- it is really slow - even an order of magnitude slower than regular code (the reasons given vary),
and
- it is messy because people expect only errors to be handled in exceptional code.
This question is about #1.
As an example, this page describes Java exception handling as "very slow" and relates the slowness to the creation of the exception message string - "this string is then used in creating the exception object that is thrown. This is not fast." The article Effective Exception Handling in Java says that "the reason for this is due to the object creation aspect of exception handling, which thereby makes throwing exceptions inherently slow". Another reason out there is that the stack trace generation is what slows it down.
My testing (using Java 1.6.0_07, Java HotSpot 10.0, on 32 bit Linux), indicates that exception handling is no slower than regular code. I tried running a method in a loop that executes some code. At the end of the method, I use a boolean to indicate whether to return or throw. This way the actual processing is the same. I tried running the methods in different orders and averaging my test times, thinking it may have been the JVM warming up. In all my tests, the throw was at least as fast as the return, if not faster (up to 3.1% faster). I am completely open to the possibility that my tests were wrong, but I haven't seen anything out there in the way of the code sample, test comparisons, or results in the last year or two that show exception handling in Java to actually be slow.
What leads me down this path was an API I needed to use that threw exceptions as part of normal control logic. I wanted to correct them in their usage, but now I may not be able to. Will I instead have to praise them on their forward thinking?
In the paper Efficient Java exception handling in just-in-time compilation, the authors suggest that the presence of exception handlers alone, even if no exceptions are thrown, is enough to prevent the JIT compiler from optimizing the code properly, thus slowing it down. I haven't tested this theory yet.
Aleksey Shipilëv did a very thorough analysis in which he benchmarks Java exceptions under various combinations of conditions:
He also compares them to the performance of checking an error code at various levels of error frequency.
The conclusions (quoted verbatim from his post) were:
Truly exceptional exceptions are beautifully performant. If you use them as designed, and only communicate the truly exceptional cases among the overwhelmingly large number of non-exceptional cases handled by regular code, then using exceptions is the performance win.
The performance costs of exceptions have two major components: stack trace construction when Exception is instantiated and stack unwinding during Exception throw.
Stack trace construction costs are proportional to stack depth at the moment of exception instantiation. That is already bad because who on Earth knows the stack depth at which this throwing method would be called? Even if you turn off the stack trace generation and/or cache the exceptions, you can only get rid of this part of the performance cost.
Stack unwinding costs depend on how lucky we are with bringing the exception handler closer in the compiled code. Carefully structuring the code to avoid deep exception handlers lookup is probably helping us get luckier.
Should we eliminate both effects, the performance cost of exceptions is that of the local branch. No matter how beautiful it sounds, that does not mean you should use Exceptions as the usual control flow, because in that case you are at the mercy of optimizing compiler! You should only use them in truly exceptional cases, where the exception frequency amortizes the possible unlucky cost of raising the actual exception.
The optimistic rule-of-thumb seems to be 10^-4 frequency for exceptions is exceptional enough. That, of course, depends on the heavy-weights of the exceptions themselves, the exact actions taken in exception handlers, etc.
The upshot is that when an exception isn't thrown, you don't pay a cost, so when the exceptional condition is sufficiently rare exception handling is faster than using an
if
every time. The full post is very much worth a read.Even if throwing an exception isn't slow, it's still a bad idea to throw exceptions for normal program flow. Used this way it is analogous to a GOTO...
I guess that doesn't really answer the question though. I'd imagine that the 'conventional' wisdom of throwing exceptions being slow was true in earlier java versions (< 1.4). Creating an exception requires that the VM create the entire stack trace. A lot has changed since then in the VM to speed things up and this is likely one area that has been improved.
Exception performance in Java and C# leaves much to be desired.
As programmers this forces us to live by the rule "exceptions should be caused infrequently", simply for practical performance reasons.
However, as computer scientists, we should rebel against this problematic state. The person authoring a function often has no idea how often it will be called, or whether success or failure is more likely. Only the caller has this information. Trying to avoid exceptions leads to unclear API idoms where in some cases we have only clean-but-slow exception versions, and in other cases we have fast-but-clunky return-value errors, and in still other cases we end up with both. The library implementor may have to write and maintain two versions of APIs, and the caller has to decide which of two versions to use in each situation.
This is kind of a mess. If exceptions had better performance, we could avoid these clunky idioms and use exceptions as they were meant to be used... as a structured error return facility.
I'd really like to see exception mechanisms implemented using techniques closer to return-values, so we could have performance closer to return values.. since this is what we revert to in performance sensitive code.
Here is a code-sample that compares exception performance to error-return-value performance.
public class TestIt {
}
And here are the results:
Checking and propagating return-values does add some cost vs the baseline-null call, and that cost is proportional to call-depth. At a call-chain depth of 8, the error-return-value checking version was about 27% slower than the basline version which did not check return values.
Exception performance, in comparison, is not a function of call-depth, but of exception frequency. However, the degredation as exception frequency increases is much more dramatic. At only a 25% error frequency, the code ran 24-TIMES slower. At an error frequency of 100%, the exception version is almost 100-TIMES slower.
This suggests to me that perhaps are making the wrong tradeoffs in our exception implementations. Exceptions could be faster, either by avoiding costly stalk-walks, or by outright turning them into compiler supported return-value checking. Until they do, we're stuck avoiding them when we want our code to run fast.
Great post about exception performance is:
https://shipilev.net/blog/2014/exceptional-performance/
Instantiating vs reusing existing, with stack trace and without, etc:
Depending on depth of stack trace:
For other details (including x64 assembler from JIT) read original blog post.
That mean Hibernate/Spring/etc-EE-shit are slow because of exceptions (xD) and rewriting app control flow away from exceptions (replace it with
continure
/break
and returningboolean
flags like in C from method call) improve performance of your application 10x-100x, depending on how often you throws them ))Just compare let's say Integer.parseInt to the following method, which just returns a default value in the case of unparseable data instead of throwing an Exception:
As long as you apply both methods to "valid" data, they both will work at approximately the same rate (even although Integer.parseInt manages to handle more complex data). But as soon as you try to parse invalid data (e.g. to parse "abc" 1.000.000 times), the difference in performance should be essential.