Which is faster, try catch or if-else in java (WRT performance)
Whic开发者_高级运维h one is faster:
Either this
try {
n.foo();
}
catch(NullPointerException ex) {
}
or
if (n != null) n.foo();
It's not a question of which is faster, rather one of correctness.
An exception is for circumstances which are exactly that, exceptional.
If it is possible for n
to be null
as part of normal business logic, then use an if..else
, else throw
an exception.
if (n != null) n.foo();
is faster.
Explicitly testing for a null pointer is much faster than using exception handling.
For the record, most of the oherheads in using exceptions are incurred in the instantiation of the exception object. In particular in the call to fillInStackTrace()
which has to:
- examine every stack frame for the current thread's stack, and
- create a data structure to capture the stack frame details.
In some cases, you can reduce this by reusing the exception object, or by overriding an application specific exception's fillInStackTrace()
method to make it a no-op. The downside in both cases is that proper stacktraces will no longer be available to help you debug unexpected exceptions. (And neither of these are applicable to the OP's example.)
While exception instantiation is expensive, exception throwing, propagation and catching are not exactly cheap either.
(I should add that I agree with @Mitch's general point about premature optimization. However, the cost of an exception that actually occurs is large enough that it is best to avoid using them for routine null
checks; i.e. if you intend to catch and recover from the NPE.)
There is a second reason why explicit null testing is a better idea. Consider this:
try {
doSomething(a.field);
} catch (NullPointerException ex) {
System.err.println("a.field is null");
}
What happens if an NPE happens within the call to doSomething(...)
instead of during the evaluation of the a.field
expression? Sure, we'll catch an NPE, but we will misdiagnose it, and then attempt to continue ... incorrectly assuming that a.field
is unset or something.
Distinguishing an "expected" NPE from an "unexpected" NPE is theoretically possible, but in practice very difficult. A much simpler and more robust approach is to explicitly test for the null
values that you are expecting (e.g. with an if
statement), and treat all NPEs as bugs.
(I'm sure that this is what @Mitch means by "treating exceptions as exceptional", but I think it helps to spell things out with an illustrative example ...)
Finally, it is worth nothing that in
if (n != null) n.foo();
there are actually two null tests:
- There is an explicit test in
n == null
. - There is also an implicit check in
n.foo()
.
However, the JIT compiler should be able to optimize away the second check and the associated native code that throws the NPE. In fact, the addition of the if (n != null)
is likely to add zero runtime overhead once the code has been compiled to native code.
The answer to this is not as simple as it looks, because this will depend on the percentage of times that the object is really null. When this is very uncommon (say in 0.1% of the time), it might even be faster. To test this I've done some benchmarking with the following results (with Java 1.6 client):
Benchmaring with factor 1.0E-4
Average time of NullIfTest: 0.44 seconds
Average time of NullExceptionTest: 0.45 seconds
Benchmaring with factor 0.0010
Average time of NullIfTest: 0.44 seconds
Average time of NullExceptionTest: 0.46 seconds
Benchmaring with factor 0.01
Average time of NullIfTest: 0.42 seconds
Average time of NullExceptionTest: 0.52 seconds
Benchmaring with factor 0.1
Average time of NullIfTest: 0.41 seconds
Average time of NullExceptionTest: 1.30 seconds
Benchmaring with factor 0.9
Average time of NullIfTest: 0.07 seconds
Average time of NullExceptionTest: 7.48 seconds
This seems pretty conclusive to me. NPE's are just very slow. (I can post the benchmarking code if wanted)
edit: I've just made an interesting discovery: when benchmarking using the server JVM, the results change drastically:
Benchmaring with factor 1.0E-4
Average time of NullIfTest: 0.33 seconds
Average time of NullExceptionTest: 0.33 seconds
Benchmaring with factor 0.0010
Average time of NullIfTest: 0.32 seconds
Average time of NullExceptionTest: 0.33 seconds
Benchmaring with factor 0.01
Average time of NullIfTest: 0.31 seconds
Average time of NullExceptionTest: 0.32 seconds
Benchmaring with factor 0.1
Average time of NullIfTest: 0.28 seconds
Average time of NullExceptionTest: 0.30 seconds
Benchmaring with factor 0.9
Average time of NullIfTest: 0.05 seconds
Average time of NullExceptionTest: 0.04 seconds
Using the server VM, the difference is hardly noticable. Still: I'd rather not use catching NullPointerException unless it really is an exception.
If n.foo()
happens to throw internally a NPE, you are off for a long debugging session (or worse, your app fails in production..). Just don't do it.
How many nano-seconds do you plan to save, anyways?
I notice I'm not the only one reading the Java Specialist's Newsletter :)
Apart from the fact that there's a semantic difference (the NPE isn't necessarily caused by dereferencing n
, it might have been thrown by some error in foo()
), and a readability issue (the try/catch is more confusing to a reader than the if
), they should be about equally fast in the case when n != null
(with the if/else version having a slight advantage), but when n == null
if/else is a lot faster. Why?
- When
n == null
, the VM must create a new exception object and fill in its stack trace. The stack trace info is really expensive to acquire, so here the try/catch version is far more expensive. - Some believe that conditional statements are slower because they prevent instruction pipelining, and by avoiding the explicit
if
they think they got away cheap whenn != null
. The thing is, however, that the VM will do an implicit null check when dereferencing... that is, unless the JIT can determine thatn
must be non-null, which it can in the if/else version. This means that the if/else and try/catch versions should be perform approximately the same. But... - ... try/catch clauses can interfere with how the JIT can inline method calls, which means that it might not be able to optimize the try/catch version as well as the if/else.
Beside the good answers (use exceptions for exceptional cases) I see that you're basically trying to avoid the null checks everywhere. Java 7 will have a "null safe" operator that will return null when n?.foo()
is called instead of throwing a NPE. That's borrowed from the Groovy language. There's also a trend to avoid using null altogether in one's code except when really needed (ie: dealing with libraries). See this other answer for more discussion on this.
Avoiding != null statements
It is usually expensive to handle exceptions. The VM Spec might give you some insight into how much, but in the above case if (n != null) n.foo();
is faster.
Although I agree with Mitch Wheat regarding the real question is correctness.
@Mitch Wheat - In his defense this is a pretty contrived example. :)
The if construct is faster. The condition can be easily translated to machine code (processor instructions).
The alternative (try-catch) requires creating a NullPointerException object.
Definitely second form is much faster. In the try-catch
scenario, it throws an exception
which does a new Exception()
of some form. Then the catch
block is invoked which is a method call and has to execute whatever code is in it. You get idea.
Firstly the if.. then .. else is better, for numerous reasons pointed out by the other posters.
However it is not necceseraly faster! It depends entirly on the ration of null objects to not null objects. It probably takes a hundreds of thousands times the resources to process an exception rather than test for null, however, if a null object occurs only once for every million objects then the exception option will be slightly faster. But not that much faster that its worth making your program less readable and harder to debug.
This issue has discussed recently by Dr. Heinz:
http://javaspecialists.eu/webinars/recordings/if-else-npe-teaser.mov
精彩评论