56

Is there any overhead in Java for using a try/catch block, as opposed to an if block (assuming that the enclosed code otherwise does not request so)?

For example, take the following two simple implementations of a "safe trim" method for strings:

public String tryTrim(String raw) {
    try {
        return raw.trim();
    } catch (Exception e) {
    }
    return null;
}

public String ifTrim(String raw) {
    if (raw == null) {
        return null;
    }
    return raw.trim();
}

If the raw input is only rarely null, is there any performance difference between the two methods?

Furthermore, is it a good programming pattern to use the tryTrim() approach for simplifying the layout of code, especially when many if blocks checking rare error conditions can be avoided by enclosing the code in one try/catch block?

For example, it is a common case to have a method with N parameters, which uses M <= N of them near its start, failing quickly and deterministically if any such parameter is "invalid" (e.g., a null or empty string), without affecting the rest of the code.

In such cases, instead of having to write k * M if blocks (where k is the average number of checks per parameter, e.g. k = 2 for null or empty strings), a try/catch block would significantly shorten the code and a 1-2 line comment could be used to explicitly note the "unconventional" logic.

Such a pattern would also speed up the method, especially if the error conditions occur rarely, and it would do so without compromising program safety (assuming that the error conditions are "normal", e.g. as in a string processing method where null or empty values are acceptable, albeit seldom in presence).

PNS
  • 19,295
  • 32
  • 96
  • 143

5 Answers5

65

I know you're asking about performance overhead, but you really should not use try/catch and if interchangeably.

try/catch is for things that go wrong that are outside of your control and not in the normal program flow. For example, trying to write to a file and the file system is full? That situation should typically be handled with try/catch.

if statements should be normal flow and ordinary error checking. So, for example, user fails to populate a required input field? Use if for that, not try/catch.

It seems to me that your example code strongly suggests that the correct approach there is an if statement and not a try/catch.

To answer your question, I would surmise that there is generally more overhead in a try/catch than an if. To know for sure, get a Java profiler and find out for the specific code you care about. It's possible that the answer may vary depending on the situation.

Trott
  • 66,479
  • 23
  • 173
  • 212
  • Correct and, as Sergey's answer indicates, there is more overhead even if the exception is never thrown. Thanks! – PNS Dec 24 '11 at 00:34
  • 3
    @PNS Not true, sergey just isn't measuring what you and sergey think he's measuring (oh I like that sentence, sorry couldn't resist ;)). There's no overhead if you don't catch an exception to a catch block. But then a Nullpointer check by the JIT is generally free (don't make an explicit check and instead catch the hw exception) so it just depends on whether the JIT is intelligent enough to do the optimization in this situation as well. Not the slightest idea, but anyways the difference is measured in clock cycles - absolutely uninteresting. – Voo Dec 24 '11 at 00:49
  • I was editing that comment when you added yours. So, would you think that the difference is zero if no exception (or just a NullPointerException) is thrown, at least in the Sun JDKs? – PNS Dec 24 '11 at 00:55
  • 3
    @PNS That has more to do with the JVM/JIT (and the processor), I can argue why try/catch should be faster in theory and then why if should be just as fast here, but then there are lots of guesses involved. There are so many variables indirectly involved here that it'd be hard to say. And writing a good benchmark for this seems extremely hard considering the amount of overhead - if **I** wanted an answer I'd believe in, I'd ask [Cliff](http://www.azulsystems.com/blog/cliff) or some other genius there :) – Voo Dec 24 '11 at 01:10
  • @Voo: Using an `if` the null check is "free", too. The JVM must check if `raw==null` anyway, and doing it once suffices. When `raw.trim()` gets executed after the `if`, there's no need to check for null anymore (except if `raw` was `volatile`). I wouldn't recommend profiling (as it has some not so small overhead which here most probably comes in the way), but I think the speed could be measured using caliper. – maaartinus Aug 21 '12 at 03:33
  • 1
    @maaartinus The whole point is that the JVM isn't required to do an explicit null check for `raw.trim()` because the hardware (well x86, depends obviously on the architecture) will tell you anyhow if `raw` happens to be null and you can worry about that then. Other than that: Yes that's exactly what I said, both can amount to the same depending on how clever the JVM is. – Voo Sep 29 '12 at 17:16
  • I'm working on a HashMap wrapper that has a create static function that takes arbitrary number of objects as inputs and casts every odd one to string and even one keeps as object. The question is very relevant because I expect an extra, and want it to be treated as String -> null. So I may as easily use ArrayOutOfBoundsException as if (args.length < i). So I want a definite answer which is the better option. Because sometimes, your expected flow IS the abnormal flaw, as is the case in all secure software. The answer is: its cheaper to handle exceptions before they happen in if statements – Dmytro Jul 15 '17 at 01:06
  • Just one side question. Given that creating the stacktrace is the most expensive operation involved and that try / catching itself is just a bit more expensive than if nowadays, what happens if the condition that triggers the exception is only met lets say once in trillions of execusions? – DGoiko Jan 26 '20 at 17:01
34

This question has almost been "answered to death", but I think there are a few more points that could usefully be made:

  • Using try / catch for non-exceptional control flow is bad style (in Java). (There is often debate about what "non-exceptional" means ... but that's a different topic.)

  • Part of the reason it is bad style is that try / catch is orders of magnitude more expensive than an regular control flow statement1. The actual difference depends on the program and the platform, but I'd expect it to be 1000 or more times more expensive. Among other things, the creation the exception object captures a stack trace, looking up and copying information about each frame on the stack. The deeper the stack is, the more that needs to be copied.

  • Another part of the reason it is bad style is that the code is harder to read.

1 - The JIT in recent versions of Java can optimize exception handling to drastically reduce the overheads in some cases. However, these optimizations are not enabled by default.

There are also issues with the way that you've written the example:

  • Catching Exception is very bad practice, because there is a chance that you will catch other unchecked exceptions by accident. For instance, if you did that around a call to raw.substring(1) you would also catch potential StringIndexOutOfBoundsExceptions ... and hide bugs.

  • What your example is trying to do is (probably) a result of poor practice in dealing with null strings. As a general principle, you should try to minimize the use of null strings, and attempt to limit their (intentional) spread. Where possible, use an empty string instead of null to mean "no value". And when you do have a case where you need to pass or return a null string, document it clearly in your method javadocs. If your methods get called with a null when they shouldn't ... it is a bug. Let it throw an exception. Don't try to compensate for the bug by (in this example) returning null.


My question was more general, not only for null values.

... and most of the points in my answer are not about null values!

But please bear in mind that there are many cases where you want to allow the occasional null value, or any other value that could produce an exception, and just ignore them. This is the case, for example, when reading key/pair values from somewhere and passing them to a method like the tryTrim() above.

Yes there are situations where null values are expected, and you need deal with them.

But I would argue that what tryTrim() is doing is (typically) the wrong way to deal with null. Compare these three bits of code:

// Version 1
String param = httpRequest.getParameter("foo");
String trimmed = tryTrim(param);
if (trimmed == null) {
    // deal with case of 'foo' parameter absent
} else {
    // deal with case of 'foo' parameter present
}

// Version 2
String param = httpRequest.getParameter("foo");
if (param == null) {
    // deal with case of 'foo' parameter absent
} else {
    String trimmed = param.trim();
    // deal with case of 'foo' parameter present
}

// Version 3
String param = httpRequest.getParameter("foo");
if (param == null) {
    // treat missing and empty parameters the same
    param = "";
}
String trimmed = param.trim();

Ultimately you have to deal with the null differently from a regular string, and it is usually a good idea do this as soon as possible. The further the null is allowed to propagate from its point origin, the more likely it is that the programmer will forget that a null value is a possibility, and write buggy code that assumes a non-null value. And forgetting that an HTTP request parameter could be missing (i.e. param == null) is a classic case where this happens.

I'm not saying that tryTrim() is inherently bad. But the fact that you feel the need write methods like this is probably indicative of less than ideal null handling.

Finally, there are other ways to model "there isn't a value" in an API. These include:

  • Test for unexpected null in setters and constructors, and either throw an exception or substitute a different value.
  • Add an is<Field>Set method ... and throwing an exception in get<Field> if the result would be null.
  • Use the Null Object Pattern.
  • Use empty strings, empty collections, zero length arrays, etc instead of null strings, collections or arrays1.
  • Using Optional.

1 - To mind, this is different from the Null Object Pattern because there isn't necessarily just one instance of the type to denote "nullity". But you can combine the two ideas ... if you are disciplined about it.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
11

Use the second version. Never use exceptions for control flow when other alternatives are available, as that is not what they are there for. Exceptions are for exceptional circumstances.

While on the topic, do not catch Exception here, and especially do not swallow it. In your case, you would expect a NullPointerException. If you were to catch something, that is what you would catch (but go back to paragraph one, do not do this). When you catch (and swallow!) Exception, you are saying "no matter what goes wrong, I can handle it. I don't care what it is." Your program might be in an irrevocable state! Only catch what you are prepared to deal with, let everything else propogate to a layer that can deal with it, even if that layer is the top layer and all it does is log the exception and then hit the eject switch.

Anthony Pegram
  • 123,721
  • 27
  • 225
  • 246
  • +1 for the point about catching `Exception`. It is really really bad practice. – Stephen C Dec 24 '11 at 01:55
  • 1
    I would like to play devil's advocate for catching Exception and logging it and rethrowing it. This can be necessary if, for example, you are somewhere in the code and an unexpected exception happens, you can catch it, log meaningful state information that wouldn't be available in a simple stack trace, and then rethrow it to upper layers to deal with. – slashdottir Jan 26 '15 at 18:55
  • 1
    @slashdottir - If you catch, log and rethrow `Exception`, the signature of the enclosing class needs to be declared as `throws Exception`. Before long you are doing that with lots of methods and lose track of what checked exceptions may be thrown. – Stephen C Jan 27 '20 at 01:55
5

Otherwise exceptions are fast to throw and catch (though an if is probably still faster), but the slow thing is creating the exception's stack trace, because it needs to walk through all of the current stack. (In general it's bad to use exceptions for control flow, but when that really is needed and the exceptions must be fast, it's possible to skip building the stack trace by overriding the Throwable.fillInStackTrace() method, or to save one exception instance and throw it repeatedly instead of always creating a new exception instance.)

Esko Luontola
  • 73,184
  • 17
  • 117
  • 128
-3

As far as overhead goes, you can test for yourself:

public class Overhead {

public static void main(String[] args) {
    String testString = "";

    long startTimeTry = System.nanoTime();
    tryTrim(testString);
    long estimatedTimeTry = System.nanoTime() - startTimeTry;

    long startTimeIf = System.nanoTime();
    ifTrim(testString);
    long estimatedTimeIf = System.nanoTime() - startTimeIf;

    System.out.println("Try time:" + estimatedTimeTry);
    System.out.println("If time:" + estimatedTimeIf);

}

public static String tryTrim(String raw) {
    try {
        return raw.trim();
    } catch (Exception e) {
    }
    return null;
}

public static String ifTrim(String raw) {
    if (raw == null) {
        return null;
    }
    return raw.trim();
}

}

The numbers I got are:

Try time:8102
If time:1956

As far as what style - it is a whole separate question. The if statement looks pretty natural, but the try looks really strange for multiple reason: - you caught Exception even though you are checking for NULL value, are you expecting something "exceptional" to happen (otherwise catch NullException)? - you caught that Exception, are you going to report it or swallow? etc. etc. etc.

Edit: See my comment for why this is an invalid test, but I really didn't want to leave this standing here. Just by swapping tryTrim and ifTrim, we suddenly get the following results (on my machine):

Try time:2043
If time:6810

Instead of starting to explain all of this, just read this for the beginning - Cliff also has some great slides about the whole topic, but I can't find the link at the moment.

Knowing how exception handling works in Hotspot, I'm fairly certain that in a correct test try/catch without an exception) would be the baseline performance (because there's no overhead whatsoever), but the JIT can play some tricks with Nullpointer checks followed by method invocations (no explicit check, but catch the hw exception if the object is null) in which case we'd get the same result. Also don't forget: We're talking about the difference of one easily predictable if which would be ONE CPU cycle! The trim call will cost a million times that.

Community
  • 1
  • 1
Sergey
  • 978
  • 2
  • 11
  • 33
  • 4
    I hope nobody here assumes that test is valid right? We've got class loading problems, timer inaccuracies (your default accuracy for timers is 10-15ms, now just think for one second what that tells us), test non-jitted code and so on. Micro benchmarks in Java are hard to get right, but this wouldn't even make sense in a compiled language... – Voo Dec 24 '11 at 00:45
  • I have read in some other posts that try/catch has about the same overhead as if, provided that no exception was thrown, but apparently this is not the case, let alone the "pattern correctness" issues. Of course, the time values measured in the test are dominated by the compiler setup times for the 2 different blocks, which would usually be made negligible by the enclosed code running times in more real-world scenarios, but it still makes a point. – PNS Dec 24 '11 at 00:50
  • 1
    @PNS Just swap the if and try blocks and suddenly the try is 4times faster than the if for me.. – Voo Dec 24 '11 at 00:54
  • 3
    @Voo is absolutely right that as a benchmark, this is pure nonsense. – Tom Anderson Dec 24 '11 at 00:59
  • I would expect someone to run those method a million times to get some results and not just once... – Betlista Sep 04 '13 at 14:44