Throwing Exceptions – slow and ugly

This post is about a historical experience in conjunction with recently applied performance optimization techniques. Years ago I was swearing at a particular application where I had to discover the undocumented behaviour buried under a truly clever engineering “technique”.

It was a typical monolithic Java EE application responsible for invoicing. The exact code is best to keep forgotten, but I recall that the developer had found a truly clever way to control the flow of a business process.

Part of the flow was a usual endless if-then-else mess, but what made things more “exciting” was that some random elements of those checks were buried into custom java.lang.RuntimeException handling mechanics. So you might have code similar to the following:

if (invoice.overdue()) {
  if (invoice.getCustomer().isKeyCustomer())
    throw new InvoiceOverdueException(InvoiceOverdueException.KEY_CUSTOMER);
  else
    doSomething();
}
else {
  if (invoice.getDueAmount() > BIG_AMOUNT)
    if (invoice.getCustomer().isKeyCustomer())
      //be silent
    else
      throw new InvoiceExceededException(invoice.getDueAmount());
}

And not short and easy to read blocks like the above but thousands of lines of code scattered along the whole application.

I guess you might agree with me that this is one hell of a good way to make yourself indispensable. There is no freakin way someone is going to understand why the application is behaving like it is.

I recalled this experience due to the recent Plumbr optimization task at hand. I would like to say our code was not using exceptions the way the previous case was describing but this would unfortunately not be completely true. One particular method still had a RuntimeException constructed and thrown during a regular flow of the code. I only discovered this lone villain due to the performance anomaly in this particular module.

Usually an exception is thrown only when facing unexpected problems. So we don’t expect exceptions to be thrown by thousands per second per thread. But as I, you might discover a method which uses exceptions for more likely events.

I only found the culprit due to the fact that this was a frequently used code block in a particular graph traversing algorithm so it was crucial to squeeze the last milliseconds out of it. Removing the exception handling immediately made this code block to complete more than 100 times faster.

This might make you wonder – why is exception handling slow? The slow part is related to constructing the exception. Or – to be more precise – any subclass of a java.lang.Throwable.

If you recall, all constructors invoke a call to superclass default constructor via calling super(). If you do not specify this call by yourself, the compiler is kind enough to add it into the bytecode itself. Anyhow, when looking into the java.lang.Throwable source code, you see the answer staring into your face:

public Throwable() {
        fillInStackTrace();
    }

    public synchronized Throwable fillInStackTrace() {
        if (stackTrace != null ||
            backtrace != null /* Out of protocol state */ ) {
            fillInStackTrace(0);
            stackTrace = UNASSIGNED_STACK;
        }
        return this;
    }

    private native Throwable fillInStackTrace(int dummy);

So each time you create a new Throwable(), you end up filling the whole stack trace through the native call. If you do not believe this is slow, execute the following jmh microbenchmark to verify that creating exceptions is hundreds of times more expensive than constructing regular objects:

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class NewExceptionTest {

  @GenerateMicroBenchmark
  public Object baseline() {
    return new Object();
  }

  @GenerateMicroBenchmark
  public Object exceptional() {
    return new RuntimeException();
  }
}
Benchmark                          Mode Thr    Cnt  Sec         Mean   Mean error    Units
j.NewExceptionTest.baseline        avgt   1      5    5        3.275        0.029  nsec/op
j.NewExceptionTest.exceptional     avgt   1      5    5     1329.266        8.675  nsec/op

To summarize this – the exceptions should be used for what the name indicates – for exceptional situations. If you start abusing the concept, you either make your code unreadable or start suffering from performance issues. At bare minimum I guarantee you get tons of negative karma from doing so.
 

Reference: Throwing Exceptions – slow and ugly from our JCG partner Nikita Salnikov Tarnovski at the Plumbr Blog blog.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

Leave a Reply


× nine = 27



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy | Contact
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close