216

We know that it is expensive to catch exceptions. But, is it also expensive to use a try-catch block in Java even if an exception is never thrown?

I found the Stack Overflow question/answer Why are try blocks expensive?, but it is for .NET.

Community
  • 1
  • 1
jsedano
  • 4,088
  • 2
  • 20
  • 31
  • As far as I know, a try block is very cheap in Java. –  May 08 '13 at 23:18
  • 32
    There really is no point to this question. Try..catch has a very specific purpose. If you need it, you need it. In any case, what point is a try without a catch? – JohnFx May 08 '13 at 23:18
  • 1
    Is it possible to even have a try without a catch unless using a finally within the block? – user2277872 May 08 '13 at 23:19
  • 54
    `try { /* do stuff */ } finally { /* make sure to release resources */ }` is legal and useful – A4L May 08 '13 at 23:21
  • 4
    That cost has to be weighed against the benefits. It doesn't stand alone. In any case, expensive is relative, and until you know that you can't do it, it makes sense to use the most obvious method rather than not do something because it might save you a millisecond or two over the course of an hour of program execution. – Joel May 08 '13 at 23:21
  • 1
    The linked question discusses that it's *not* the `try` that is "expensive" but dealing with the exception and unwinding the stack. If you need to deal with exceptions, you need to deal with exceptions .. – user2246674 May 08 '13 at 23:23
  • @A4L never knew that was even possible, interesting. – raffian May 08 '13 at 23:23
  • @user2246674 sometime you open resources in a method witch you absolutely want to close even if the method throws at some point an exception. The finally block is guarantied to execute. – A4L May 08 '13 at 23:24
  • 4
    I hope this isn't a lead to a "let's-reinvent-error-codes" type situation... – mikołak May 08 '13 at 23:25
  • 6
    @SAFX: with Java7 you can even get rid of the `finally` block using a `try-with-resources` –  May 08 '13 at 23:29
  • 3
    This answer: http://stackoverflow.com/a/10978562/330315 claims that the try block itself does not have any cost at all. –  May 08 '13 at 23:32
  • 2
    @JohnFx I interpreted the question as being about try-catch: "... use a try catch block ...", but with the catch block very rarely, if ever, executed. This question could affect the cost of declaring a method to throw a checked exception. – Patricia Shanahan May 08 '13 at 23:44
  • 3
    @JohnFx There is a point to this question. If `try` was expensive, then you might offer an alternative method on a class that returned a result instead of throwing an exception. E.g. from C#: `int.Parse()` vs `int.TryParse(...)`. – Ergwun May 09 '13 at 02:34
  • 1
    @JohnFx Not a try without a catch, but a try-catch with an exception that rarely fires. – jsedano May 09 '13 at 14:30
  • 1
    I don't understand how this question has in only a few days 48 upvotes and its answer as well, and 2K+ views? Some kind of ad caimpaign?? . As the most popular comment says, it's pointless! If the exception needs to be handled the catch has to be there; what are you going to compare the 'expensiveness' with? with a crash? – quinestor May 15 '13 at 06:35
  • 2
    @quinestor that comment is wrong, please read on, I was asking, if we have a try-catch block, but the exception never occurs (or rarely), does it hurts the performance of the code inside the try... – jsedano May 15 '13 at 14:34
  • 1
    @Jesse, this question is about java, and this one http://stackoverflow.com/questions/1308432/do-try-catch-blocks-hurt-performance-when-exceptions-are-not-thrown, is about C# – jsedano May 15 '13 at 14:35
  • @JohnFx 'never occurs' = is not thrown, then the try-catch block shouldn't be there. 'rarely' 'hurt performance'? what's the performance comparison? compared to not handle an exception and have a crash? – quinestor May 15 '13 at 14:40
  • @anakata You are correct. I completely missed that. My mistake. – Jesse May 15 '13 at 14:47
  • 1
    @JohnFx `try-finally` (or, with ARM in Java, simply `try`) blocks abound in good code. You definitely don't need `catch` to be useful, and most problems with exceptions stem from improper `catch` blocks. – erickson May 24 '13 at 22:44

7 Answers7

232

try has almost no expense at all. Instead of doing the work of setting up the try at runtime, the code's metadata is structured at compile time such that when an exception is thrown, it now does a relatively expensive operation of walking up the stack and seeing if any try blocks exist that would catch this exception. From a layman's perspective, try may as well be free. It's actually throwing the exception that costs you - but unless you're throwing hundreds or thousands of exceptions, you still won't notice the cost.


try has some minor costs associated with it. Java cannot do some optimizations on code in a try block that it would otherwise do. For example, Java will often re-arrange instructions in a method to make it run faster - but Java also needs to guarantee that if an exception is thrown, the method's execution is observed as though its statements, as written in the source code, executed in order up to some line.

Because in a try block an exception can be thrown (at any line in the try block! Some exceptions are thrown asynchronously, such as by calling stop on a Thread (which is deprecated), and even besides that OutOfMemoryError can happen almost anywhere) and yet it can be caught and code continue to execute afterwards in the same method, it is more difficult to reason about optimizations that can be made, so they are less likely to happen. (Someone would have to program the compiler to do them, reason about and guarantee correctness, etc. It'd be a big pain for something meant to be 'exceptional') But again, in practice you won't notice things like this.

Patashu
  • 21,443
  • 3
  • 45
  • 53
  • 2
    *Some exceptions are thrown asynchronously*, they are not async but thrown in safe points. and this part *try has some minor costs associated with it. Java cannot do some optimizations on code in a try block that it would otherwise do* does need a serious reference. At some point the code is very likely to be within try/catch block. It might be true that try/catch block would be harder to be inlined and building proper lattice for the result but the part w/ the rearrange is ambiguous. – bestsss May 23 '13 at 21:32
  • 2
    Does a `try...finally` block without `catch` also prevent some optimizations? – dajood Sep 30 '15 at 14:51
  • 6
    @Patashu *"It's actually throwing the exception that costs you"* Technically, **throwing** the exception is not expensive; instantiating the `Exception` object is what takes most of the time. – Austin Mar 31 '16 at 21:20
82

Let's measure it, shall we?

public abstract class Benchmark {

    final String name;

    public Benchmark(String name) {
        this.name = name;
    }

    abstract int run(int iterations) throws Throwable;

    private BigDecimal time() {
        try {
            int nextI = 1;
            int i;
            long duration;
            do {
                i = nextI;
                long start = System.nanoTime();
                run(i);
                duration = System.nanoTime() - start;
                nextI = (i << 1) | 1;
            } while (duration < 100000000 && nextI > 0);
            return new BigDecimal((duration) * 1000 / i).movePointLeft(3);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public String toString() {
        return name + "\t" + time() + " ns";
    }

    public static void main(String[] args) throws Exception {
        Benchmark[] benchmarks = {
            new Benchmark("try") {
                @Override int run(int iterations) throws Throwable {
                    int x = 0;
                    for (int i = 0; i < iterations; i++) {
                        try {
                            x += i;
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    return x;
                }
            }, new Benchmark("no try") {
                @Override int run(int iterations) throws Throwable {
                    int x = 0;
                    for (int i = 0; i < iterations; i++) {
                        x += i;
                    }
                    return x;
                }
            }
        };
        for (Benchmark bm : benchmarks) {
            System.out.println(bm);
        }
    }
}

On my computer, this prints something like:

try     0.598 ns
no try  0.601 ns

At least in this trivial example, the try statement had no measurable impact on performance. Feel free to measure more complex ones.

Generally speaking, I recommend not to worry about the performance cost of language constructs until you have evidence of an actual performance problem in your code. Or as Donald Knuth put it: "premature optimization is the root of all evil".

meriton
  • 68,356
  • 14
  • 108
  • 175
  • 8
    while try/no try is very likely to be the same on most JVM, the microbenchmark is terribly flawed. – bestsss May 23 '13 at 21:24
  • 2
    quite a few levels: you mean the results are computed in under 1ns? Compiled code will remove both the try/catch AND the loop altogether (summing number from 1 to n is a trivial arithmetic progression sum). Even if the code contains try/finally the compiler can prove, there is nothing to be thrown in there. The abstract code has only 2 call sites and it will be clone and inlined. There are more cases, just look up some articles on microbenchmark and it you decide to write a microbenchmark *always* check the generated assembly. – bestsss May 24 '13 at 06:20
  • 3
    The reported times are *per iteration* of the loop. As a measurement will only be used if it has a total elapsed time > 0.1 seconds (or 2 billion iterations, which wasn't the case here) I find your assertion that the loop has been removed in its entirety hard to believe - because if the loop was removed, what took 0.1 seconds to execute? – meriton May 24 '13 at 08:34
  • ... and indeed, according to `-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly`, both the loop and the addition therein are present in the generated native code. And no, the abstract methods are not inlined, because their caller is not just in time compiled (presumably, because it isn't invoked enough times). – meriton May 24 '13 at 09:18
  • How do I write a correct micro-benchmark in Java: https://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java – Vadzim Apr 14 '19 at 13:09
  • @Vadzim: I know that link ... which of the best practices therein do you want to bring to my attention? As far as I know, the above benchmarking harness fullfills them all ... with the exception of using a library, because linking a library in a stack overflow answer makes it harder to verify my measurements. – meriton Apr 15 '19 at 03:10
52

try/catch may have some impact on performance. This is because it prevents JVM from doing some optimizations. Joshua Bloch, in "Effective Java," said the following:

• Placing code inside a try-catch block inhibits certain optimizations that modern JVM implementations might otherwise perform.

Evgeniy Dorofeev
  • 133,369
  • 30
  • 199
  • 275
  • 29
    "it prevents JVM from doing some optimizations"...? Could you elaborate at all? – The Kraken May 08 '13 at 23:20
  • 5
    @The Kraken Code inside of try blocks (usually? always?) can't be re-ordered with code outside of try blocks, as one example. – Patashu May 08 '13 at 23:24
  • 3
    Note that the question was whether "it's expensive", not whether "it has any impact on performance". – mikołak May 08 '13 at 23:29
  • 4
    *added an excerpt from Effective Java*, and that's the bible of java, of course; unless there is a reference the excerpt doesn't tell anything. Practically any code in java is within try/finally at some level. – bestsss May 23 '13 at 21:23
34

Yep, as the others have said, a try block inhibits some optimizations across the {} characters surrounding it. In particular, the optimizer must assume that an exception could occur at any point within the block, so there's no assurance that statements get executed.

For example:

    try {
        int x = a + b * c * d;
        other stuff;
    }
    catch (something) {
        ....
    }
    int y = a + b * c * d;
    use y somehow;

Without the try, the value calculated to assign to x could be saved as a "common subexpression" and reused to assign to y. But because of the try there is no assurance that the first expression was ever evaluated, so the expression must be recomputed. This isn't usually a big deal in "straight-line" code, but can be significant in a loop.

It should be noted, however, that this applies ONLY to JITCed code. javac does only a piddling amount of optimization, and there is zero cost to the bytecode interpreter to enter/leave a try block. (There are no bytecodes generated to mark the block boundaries.)

And for bestsss:

public class TryFinally {
    public static void main(String[] argv) throws Throwable {
        try {
            throw new Throwable();
        }
        finally {
            System.out.println("Finally!");
        }
    }
}

Output:

C:\JavaTools>java TryFinally
Finally!
Exception in thread "main" java.lang.Throwable
        at TryFinally.main(TryFinally.java:4)

javap output:

C:\JavaTools>javap -c TryFinally.class
Compiled from "TryFinally.java"
public class TryFinally {
  public TryFinally();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]) throws java.lang.Throwable;
    Code:
       0: new           #2                  // class java/lang/Throwable
       3: dup
       4: invokespecial #3                  // Method java/lang/Throwable."<init>":()V
       7: athrow
       8: astore_1
       9: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      12: ldc           #5                  // String Finally!
      14: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      17: aload_1
      18: athrow
    Exception table:
       from    to  target type
           0     9     8   any
}

No "GOTO".

kevinarpe
  • 20,319
  • 26
  • 127
  • 154
Hot Licks
  • 47,103
  • 17
  • 93
  • 151
  • *There are no bytecodes generated to mark the block boundaries* this is not necessarily -- it does require GOTO to leave the block, otherwise it will fall into the `catch/finally` frame. – bestsss May 24 '13 at 06:34
  • @bestsss - Even if a GOTO is generated (which is not a given) the cost of that is miniscule, and it's far from being a "marker" for a block boundary -- GOTO can be generated for many constructs. – Hot Licks May 24 '13 at 10:41
  • I never mentioned cost, however *there are no bytecodes generated* is a false statement. That's all. Actually, there are no blocks in bytecode, frames do not equal blocks. – bestsss May 24 '13 at 12:07
  • There will not be a GOTO if the try falls directly into the finally, and there are other scenarios where there will be no GOTO. The point is that there is nothing on the order of "enter try"/"exit try" bytecodes. – Hot Licks May 24 '13 at 14:10
  • *There will not be a GOTO if the try falls directly into the finally* - False! there is no `finally` in bytecode, it's `try/catch(Throwable any){...; throw any;}` And It does have catch statement w/ a frame and Throwable that MUST BE defined (non null) and so on. Why do you try to argue about the topic, you can check at least some bytecode? The current guideline for impl. of finally is copying the blocks and avoiding the goto section (previous impl) but the bytecodes have to be copied depending how many exit points there are. – bestsss May 24 '13 at 18:29
  • I didn't say there was a finally in bytecode. There's one in Java, though. – Hot Licks May 24 '13 at 21:14
9

Yet another microbenchmark (source).

I created a test in which I measure try-catch and no-try-catch code version based on an exception percentage. 10% percentage means that 10% of the test cases had division by zero cases. In one situation it is handled by a try-catch block, in the other by a conditional operator. Here is my results table:

OS: Windows 8 6.2 x64
JVM: Oracle Corporation Java HotSpot(TM) 64-Bit Server VM 23.25-b01
Percentage | Result (try/if, ns)   
    0%     |      88/90   
    1%     |      89/87    
    10%    |      86/97    
    90%    |      85/83   

Which says that there is no significant difference between any of these cases.

Andrey Chaschev
  • 16,160
  • 5
  • 51
  • 68
8

To understand why the optimizations cannot be performed, It is useful to understand the underlying mechanisms. The most succinct example I could find was implemented in C macros at: http://www.di.unipi.it/~nids/docs/longjump_try_trow_catch.html

#include <stdio.h>
#include <setjmp.h>
#define TRY do{ jmp_buf ex_buf__; switch( setjmp(ex_buf__) ){ case 0: while(1){
#define CATCH(x) break; case x:
#define FINALLY break; } default:
#define ETRY } }while(0)
#define THROW(x) longjmp(ex_buf__, x)

Compilers often have difficulty determining if a jump can be localized to X, Y and Z so they skip optimizations that they can't guarantee to be safe, but the implementation itself is rather light.

technosaurus
  • 7,676
  • 1
  • 30
  • 52
  • 4
    These C macros you've found for try/catch are not equivalent to the Java or to the C# implementation, which emit 0 run time instructions. – Patashu May 10 '13 at 03:40
  • The java implementation is too extensive to include in full, this is a simplified implementation for the purpose of understanding the basic idea of how exceptions can be implemented. Saying that it emits 0 run time instructions is misleading. For example a simple classcastexception extends runtimeexception which extends exception which extends throwable which involves: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7-b147/java/lang/Throwable.java ... That's like saying a switch-case in C is free if only 1 case is ever used, there is still a small startup overhead. – technosaurus May 10 '13 at 08:39
  • The overhead of `try` is consumed at compile time, similar to how a compiler will consume the cost of computing `int a = 1 + 1;` as the constant `2` at compile time, rather than it being done at run time. There's undeniably complexity, but if no exception is thrown, nothing extra is done at run time. – Patashu May 10 '13 at 09:41
  • 1
    @Patashu All of those precompiled bits still have to be loaded at startup whether or not they are ever used. There is no way to know if there will be an out of memory exception during run time at compile time - that is why they are called run time exceptions - otherwise they would be compiler warnings/errors, so no it doesn't optimize everything away, all of the code to handle them is included in the compiled code and has a startup cost. – technosaurus May 10 '13 at 10:48
  • On some platforms (not all) a compiler can optimize the intended operation along with the comparison/jump into a single instruction, so you may never see the overhead on x86 see: http://unixwiz.net/techtips/x86-jumps.html , but might on MIPS/ARM where there aren't as many combined compare/jump instructions. It is not a huge overhead, but ~equivalent of checking a return value in C with a switch case with the non-error result being the "default:" case. But if you can prove otherwise I would be extremely interested in seeing such voodoo code that can do stuff without there being code for it. – technosaurus May 10 '13 at 10:49
  • 2
    I can't speak about C. In C# and Java, try is implemented by adding metadata, not code. When a try block is entered, nothing is executed to indicate this - when an exception is thrown, the stack is unwinded and the metadata checked for handlers of that exception type (expensive). – Patashu May 10 '13 at 11:10
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/29729/discussion-between-technosaurus-and-patashu) – technosaurus May 10 '13 at 12:02
  • 1
    Yep, I've actually implemented a Java interpreter & static bytecode compiler and worked on a subsequent JITC (for IBM iSeries) and I can tell you there's nothing that "marks" the entry/exit of the try range in the bytecodes, but rather the ranges are identified in a separate table. The interpreter does nothing special for a try range (until an exception is raised). A JITC (or static bytecode compiler) must be aware of the boundaries to suppress optimizations as previously stated. – Hot Licks May 24 '13 at 14:14
5

I have found catching NullPointException quite expensive. For 1.2k operations the time was 200ms and 12ms when I handeled it the same way with if(object==null) which was pretty improvement for me.

Mateusz Kaflowski
  • 2,221
  • 1
  • 29
  • 35