What are the effects of exceptions on performance in Java? What are the effects of exceptions on performance in Java? java java

What are the effects of exceptions on performance in Java?


It depends how exceptions are implemented. The simplest way is using setjmp and longjmp. That means all registers of the CPU are written to the stack (which already takes some time) and possibly some other data needs to be created... all this already happens in the try statement. The throw statement needs to unwind the stack and restore the values of all registers (and possible other values in the VM). So try and throw are equally slow, and that is pretty slow, however if no exception is thrown, exiting the try block takes no time whatsoever in most cases (as everything is put on the stack which cleans up automatically if the method exists).

Sun and others recognized, that this is possibly suboptimal and of course VMs get faster and faster over the time. There is another way to implement exceptions, which makes try itself lightning fast (actually nothing happens for try at all in general - everything that needs to happen is already done when the class is loaded by the VM) and it makes throw not quite as slow. I don't know which JVM uses this new, better technique...

...but are you writing in Java so your code later on only runs on one JVM on one specific system? Since if it may ever run on any other platform or any other JVM version (possibly of any other vendor), who says they also use the fast implementation? The fast one is more complicated than the slow one and not easily possible on all systems. You want to stay portable? Then don't rely on exceptions being fast.

It also makes a big difference what you do within a try block. If you open a try block and never call any method from within this try block, the try block will be ultra fast, as the JIT can then actually treat a throw like a simple goto. It neither needs to save stack-state nor does it need to unwind the stack if an exception is thrown (it only needs to jump to the catch handlers). However, this is not what you usually do. Usually you open a try block and then call a method that might throw an exception, right? And even if you just use the try block within your method, what kind of method will this be, that does not call any other method? Will it just calculate a number? Then what for do you need exceptions? There are much more elegant ways to regulate program flow. For pretty much anything else but simple math, you will have to call an external method and this already destroys the advantage of a local try block.

See the following test code:

public class Test {    int value;    public int getValue() {        return value;    }    public void reset() {        value = 0;    }    // Calculates without exception    public void method1(int i) {        value = ((value + i) / i) << 1;        // Will never be true        if ((i & 0xFFFFFFF) == 1000000000) {            System.out.println("You'll never see this!");        }    }    // Could in theory throw one, but never will    public void method2(int i) throws Exception {        value = ((value + i) / i) << 1;        // Will never be true        if ((i & 0xFFFFFFF) == 1000000000) {            throw new Exception();        }    }    // This one will regularly throw one    public void method3(int i) throws Exception {        value = ((value + i) / i) << 1;        // i & 1 is equally fast to calculate as i & 0xFFFFFFF; it is both        // an AND operation between two integers. The size of the number plays        // no role. AND on 32 BIT always ANDs all 32 bits        if ((i & 0x1) == 1) {            throw new Exception();        }    }    public static void main(String[] args) {        int i;        long l;        Test t = new Test();        l = System.currentTimeMillis();        t.reset();        for (i = 1; i < 100000000; i++) {            t.method1(i);        }        l = System.currentTimeMillis() - l;        System.out.println(            "method1 took " + l + " ms, result was " + t.getValue()        );        l = System.currentTimeMillis();        t.reset();        for (i = 1; i < 100000000; i++) {            try {                t.method2(i);            } catch (Exception e) {                System.out.println("You'll never see this!");            }        }        l = System.currentTimeMillis() - l;        System.out.println(            "method2 took " + l + " ms, result was " + t.getValue()        );        l = System.currentTimeMillis();        t.reset();        for (i = 1; i < 100000000; i++) {            try {                t.method3(i);            } catch (Exception e) {                // Do nothing here, as we will get here            }        }        l = System.currentTimeMillis() - l;        System.out.println(            "method3 took " + l + " ms, result was " + t.getValue()        );    }}

Result:

method1 took 972 ms, result was 2method2 took 1003 ms, result was 2method3 took 66716 ms, result was 2

The slowdown from the try block is too small to rule out confounding factors such as background processes. But the catch block killed everything and made it 66 times slower!

As I said, the result will not be that bad if you put try/catch and throw all within the same method (method3), but this is a special JIT optimization I would not rely upon. And even when using this optimization, the throw is still pretty slow. So I don't know what you are trying to do here, but there is definitely a better way of doing it than using try/catch/throw.


FYI, I extended the experiment that Mecki did:

method1 took 1733 ms, result was 2method2 took 1248 ms, result was 2method3 took 83997 ms, result was 2method4 took 1692 ms, result was 2method5 took 60946 ms, result was 2method6 took 25746 ms, result was 2

The first 3 are the same as Mecki's (my laptop is obviously slower).

method4 is identical to method3 except that it creates a new Integer(1) rather than doing throw new Exception().

method5 is like method3 except that it creates the new Exception() without throwing it.

method6 is like method3 except that it throws a pre-created exception (an instance variable) rather than creating a new one.

In Java much of the expense of throwing an exception is the time spent gathering the stack trace, which occurs when the exception object is created. The actual cost of throwing the exception, while large, is considerably less than the cost of creating the exception.


Aleksey Shipilëv did a very thorough analysis in which he benchmarks Java exceptions under various combinations of conditions:

  • Newly created exceptions vs pre-created exceptions
  • Stack trace enabled vs disabled
  • Stack trace requested vs never requested
  • Caught at the top level vs rethrown at every level vs chained/wrapped at every level
  • Various levels of Java call stack depth
  • No inlining optimizations vs extreme inlining vs default settings
  • User-defined fields read vs not read

He also compares them to the performance of checking an error code at various levels of error frequency.

The conclusions (quoted verbatim from his post) were:

  1. Truly exceptional exceptions are beautifully performant. If you use them as designed, and only communicate the truly exceptional cases among the overwhelmingly large number of non-exceptional cases handled by regular code, then using exceptions is the performance win.

  2. The performance costs of exceptions have two major components: stack trace construction when Exception is instantiated and stack unwinding during Exception throw.

  3. Stack trace construction costs are proportional to stack depth at the moment of exception instantiation. That is already bad because who on Earth knows the stack depth at which this throwing method would be called? Even if you turn off the stack trace generation and/or cache the exceptions, you can only get rid of this part of the performance cost.

  4. Stack unwinding costs depend on how lucky we are with bringing the exception handler closer in the compiled code. Carefully structuring the code to avoid deep exception handlers lookup is probably helping us get luckier.

  5. Should we eliminate both effects, the performance cost of exceptions is that of the local branch. No matter how beautiful it sounds, that does not mean you should use Exceptions as the usual control flow, because in that case you are at the mercy of optimizing compiler! You should only use them in truly exceptional cases, where the exception frequency amortizes the possible unlucky cost of raising the actual exception.

  6. The optimistic rule-of-thumb seems to be 10^-4 frequency for exceptions is exceptional enough. That, of course, depends on the heavy-weights of the exceptions themselves, the exact actions taken in exception handlers, etc.

The upshot is that when an exception isn't thrown, you don't pay a cost, so when the exceptional condition is sufficiently rare exception handling is faster than using an if every time. The full post is very much worth a read.