Is it expensive to use try-catch blocks even if an exception is never thrown?

JavaPerformanceTry Catch

Java Problem Overview


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.

Java Solutions


Solution 1 - Java

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.

Solution 2 - Java

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".

Solution 3 - Java

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.

Solution 4 - Java

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".

Solution 5 - Java

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.

Solution 6 - Java

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.

Solution 7 - Java

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionjsedanoView Question on Stackoverflow
Solution 1 - JavaPatashuView Answer on Stackoverflow
Solution 2 - JavameritonView Answer on Stackoverflow
Solution 3 - JavaEvgeniy DorofeevView Answer on Stackoverflow
Solution 4 - JavaHot LicksView Answer on Stackoverflow
Solution 5 - JavaAndrey ChaschevView Answer on Stackoverflow
Solution 6 - JavatechnosaurusView Answer on Stackoverflow
Solution 7 - JavaMateusz KaflowskiView Answer on Stackoverflow