Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Speed of exceptions in Java

Tags:

java

exception

Using exceptions for normal flow of code is bad - it's slow, it's bad code quality, and we've all had this drummed into our heads from day 1. At least I have! This makes sense as well, exceptions generate stack traces each time they're called, stack traces take a long time to generate and thus an exception being thrown and caught is much slower than an equivalent if statement.

So I decided to make a quick example to demonstrate this.

public static void main(String[] args) throws Exception {
    Object[] objs = new Object[500000];
    for (int i = 0; i < objs.length; i++) {
        if (Math.random() < 0.5) {
            objs[i] = new Object();
        }
    }
    long cur = System.currentTimeMillis();
    for (Object o : objs) {
        try {
            System.out.print(o.getClass());
        }
        catch (Exception e) {
            System.out.print("null");
        }
    }
    System.err.println("Try / catch: "+(System.currentTimeMillis() - cur));
    cur = System.currentTimeMillis();
    for (Object o : objs) {
        if(o==null) {
            System.out.print("null");
        }
        else {
            System.out.print(o.getClass());
        }
    }
    System.err.println("If: "+(System.currentTimeMillis() - cur));
}

However, on running the code I was shocked to see the following:

Try / catch: 11204
If: 11953

I ran the code again, this time "if" was faster:

Try / catch: 12188
If: 12187

...but only by a ms.

This is all run on the server VM, I know the majority of the time will be taken up by the print() statements, and some runs show if as a bit faster than try / catch. But regardless, surely they shouldn't even be close? And how on earth could generating stack traces be faster than a single if statement?

EDIT: To clarify, this question is a purely academic one - I know full well using exceptions for the normal flow of code is bad and would never do so!

like image 265
Michael Berry Avatar asked Jan 20 '23 15:01

Michael Berry


2 Answers

This was discussed recently in the Java Specialists newletter: http://www.javaspecialists.eu/archive/Issue187.html

Short answer: it's not generating the stack trace each time, but caching an instance of the exception.

like image 173
Peter Taylor Avatar answered Jan 23 '23 05:01

Peter Taylor


Because the exception throw point and catch point are in the same scope, and the JIT can prove that you never actually look at the stack trace, it's possible to optimize the exception into a single branch. You may be able to avoid this by explicitly throwing an exception of custom type, ideally across a function call boundary. To control against the allocation cost, in the non-throw case be sure to construct an instance of said exception object anyway (and ideally return it, to ensure escape analysis can't convert it to a stack allocation).

That said, the JIT can do all kinds of weird things. It's best to try a test case as similar as possible to Real Code, not an artificial case like this, so you can see the benchmark results after any optimizations the JIT can apply to your real code.

like image 35
bdonlan Avatar answered Jan 23 '23 05:01

bdonlan