Is it expensive to use try-catch blocks even if an exception is never thrown?
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.
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".
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.