How long does throwing an Exception take?

Overview

It is best practice to use Exceptions for exceptional conditions.

Another reason given to avoid Exceptions is for performance reasons. However how long does creating and throwing an exception take?

Breaking it down

A common way to throw exceptions is to create and throw the exception in one operation.
throw new RuntimeException();
However these can be separated, though it is rarely useful to do so.
RuntimeException re = new RuntimeException(); // take stack trace here.
// later
throw re;
The stack trace is snapshot'ed in where the exception was created, not where it was thrown which can be confusing. It is filled in later only if it is needed for performance reason.

Timing creating and throwing

A micro benchmark prints the following on a 2.67 GHz Xeon with Java 6 update 25.
Creating a Throwable took 0.8 us on average
Optimised Throw/catch a Throwable took 0.007 us on average
Throw/catch a Throwable took 0.4 us on average
Time to printStackTrace for a short stack took 4.1 us on average
Creating and throwing an exception takes about 1.2 us which is small enough not to worry about for most applications (provided it doesn't occur very often)

Summary

Exceptions should be kept to a minimum for good design, however the performance cost is not as high you might expect and may not be a valid reason alone to avoid exception in your project.

The Micro-Benchmark

Note: In at least one micro-benchmark the JVM is smart enough to optimise the loop away so its doesn't incur the cost of throwing the exception.
long start1 = 0;
Throwable t = null;
int runs = 1000 * 1000;
for (int i = -10000; i < runs; i++) {
    if (i == 0) start1 = System.nanoTime();
    t = new Throwable();
}
long time1 = System.nanoTime() - start1;
System.out.printf("Creating a Throwable took %.1f us on average%n", time1 / 1e3 / runs);

long start2 = 0;
for (int i = -10000; i < runs; i++) {
    if (i == 0) start2 = System.nanoTime();
    try {
        throw t;
    } catch (Throwable ignored) {
    }
}
long time2 = System.nanoTime() - start2;
System.out.printf("Optimised Throw/catch a Throwable took %.3f us on average%n", time2 / 1e3 / runs);

long start3 = 0;
for (int i = -10000; i < runs; i++) {
    if (i == 0) start3 = System.nanoTime();
    try {
        Thread.currentThread().stop(t);
    } catch (Exception e) {
        e.printStackTrace();
    } catch (Throwable ignored) {
    }
}
long time3 = System.nanoTime() - start3;
System.out.printf("Throw/catch a Throwable took %.1f us on average%n", time3 / 1e3 / runs);

long start4 = 0;
for (int i = -10000; i < runs; i++) {
    if (i == 0) start4 = System.nanoTime();
    StringWriter out = new StringWriter();
    t.printStackTrace(new PrintWriter(out));
    String s = out.toString();
}
long time4 = System.nanoTime() - start4;
System.out.printf("Time to printStackTrace for a short stack took %.1f us on average%n", time4 / 1e3 / runs);

Comments

Popular posts from this blog

Low Latency Microservices, A Retrospective

Unusual Java: StackTrace Extends Throwable

System wide unique nanosecond timestamps