It was always told me that Java exception handling is quite expensive.
I'm asking if is it a good practice creating an exception instance of a specific type at the beginning of the program and without creating a new one, throwing always the same exception object.
I just want to make an example. Common code:
if (!checkSomething(myObject))
throw new CustomException("your object is invalid");
alternative:
static CustomException MYEXP = new CustomException("your object is invalid");
//somewhere else
if (!checkSomething(myObject))
throw MYEXP;
Of course, I'm doing some assumptions here:
MyCustomException
has no parametersSo questions are:
I hope this is not a idle/stupid question, I'm curious about this. I think that real cost in exception handling is handling and not creation.
edit Added reference of precise discussion on FOSDEM presentation
disclaimer: none of my code works like proposed and I have no intention to manage exception like this, I'm just doing a "what-if" question and this curiosity is generated from the affermation that video. I thought: if it's done in Scala, why is not in Java?
No, don't do that. The expensive part is not handling the exception, it is generating the stacktrace. Unfortunately the stacktrace is also the useful part. If you throw a saved exception you will be passing on a misleading stacktrace.
It could be that within the implementation of Scala there are situations where it makes sense to do this. (Maybe they are doing something recursive and want to generate an exception object upfront so in case they run out of memory they can still produce an exception.) They also have a lot of information about what they're doing so they have a better chance of getting it right. But optimizations made by JVM language implementors are a very special case.
So you wouldn't be breaking anything, unless you think providing misleading information constitutes breakage. It seems like a big risk to me.
Trying out Thomas Eding's suggestion for how to create an exception with no stacktrace seems to work:
groovy:000> class MyException extends Exception {
groovy:001> public Throwable fillInStackTrace() {}}
===> true
groovy:000> e = new MyException()
===> MyException
groovy:000> Arrays.asList(e.stackTrace)
===> []
Also check out the JLS:
The NullPointerException (which is a kind of RuntimeException) that is thrown by method blowUp is not caught by the try statement in main, because a NullPointerException is not assignable to a variable of type BlewIt. This causes the finally clause to execute, after which the thread executing main, which is the only thread of the test program, terminates because of an uncaught exception, which typically results in printing the exception name and a simple backtrace. However, a backtrace is not required by this specification.
The problem with mandating a backtrace is that an exception can be created at one point in the program and thrown at a later one. It is prohibitively expensive to store a stack trace in an exception unless it is actually thrown (in which case the trace may be generated while unwinding the stack). Hence we do not mandate a back trace in every exception.
Q1. Is this a good practice?
Not in my book. It adds complexity and hinders diagnostic (see my answer to Q2).
Q2. Does this damage some JVM mechanism?
You won't get a meaningful stack trace from such an exception object.
Q3. If 1 is yes, there are performances gain? (I think not, but not sure)
Q4. If 1 and 3 are yes, why is not sponsored as practice?
Due to the problems outlined above.
Q5. If 1 is no, why Martin Odersky told in his introduction to Scala that this is how Scala works in some cases? (sorry, but I can't remember the context of this affirmation at the moment) Fosdem 2009
Hard to answer without the context.
You can do that, but the exception
must have no stacktrace, since the initial stacktrace will only serve to confuse in subsequent uses.
must not accept suppressed exceptions. if multiple threads try to add suppressed exceptions to it, things will corrupt.
So your exception constructor must do
super(msg, cause, /*enableSuppression*/false, /*writableStackTrace*/false);
Now, is it useful? Yes, otherwise why would these two boolean flags exist in the first place?:)
In some complicated cases, exception can be used as a flow control device, it may produce a simpler, faster code. Such exceptions are known as "control exceptions".
If an exception is really to indicate something exceptionally wrong with the program, then use the traditional exception.
Even though Exceptions are relatively expensive and should be kept to a minimum, they don't cost so much that you should do obtuse things "for performance purposes" This is so often a bad excuse that it is even considered by some that premature optimisation should be avoided at all costs. While that is not entirely true, you can measure how slow exceptions are.
long start = System.nanoTime();
int exceptionCount = 0;
for (int i = 0; i < 20000; i++)
try {
int j = i / (i & 1);
} catch (ArithmeticException ae) {
exceptionCount++;
}
long time = System.nanoTime() - start;
System.out.printf("Each exception took average of %,d ns%n", time / exceptionCount);
prints what I believe is a reasonable estimate.
Each exception took average of 3,064 ns
Note: as the number of loops increases, the Exception is optimised away. i.e. for a 10x the iterations
Each exception took average of 327 ns
and for 10x more
Each exception took average of 35 ns
and for 10x more
Each exception took average of 5 ns
If the exception is thrown enough, it appears the JIT is smart enough to optimise the Exception away.
if
it is necessarythen
somethingelse
is wrong. - NominSim