Java Exceptions are notoriously slow. Heck -
everything in Java is slow, but exception handling is slow in most languages and doubly slow in Java.
Why is this a problem? Because when coding in Java, exceptions are everywhere you look. Given their profuseness they turn out to be pretty useful beasts. So what can we do to improve their performance?
First, let's start off by trying to figure out what an exception does and why it takes time:
Life Of An Exception
- Our little exception starts off somewhere deep inside our program. The JVM runtime recognizes that an alternate flow has been initiated and takes over.
- The runtime unwinds the stack, frame by frame, until it finds an active
try/catch block. It then checks to see if any catch clause can handle the exception it is holding.
- If it doesn't find any, the JVM looks for a "finally" block, executes it, and continues unwinding the stack. As it unwinds the stack, it keeps a trace in the exception it is carrying.
- When it finds a matching clause, the JVM hands control back to the
catch clause along with the exception it has been carrying around.
When we examine this flow, it seems pretty clean. Worse, there doesn't seem to be anything we can do to as programmers
using the JVM (as opposed to tinkering around with it). However, if we look a bit closer, there
is something we can do.
The key here is to notice that the JVM keeps a stack trace of the exception in every
throwable object by default. This trace is very useful to the developer when the exception is finally dumped because he/she can trace where the exception came from.
In many cases, however, the exception is caught deep within the code and the developer initiates some flow that allows the user to proceed without core dumping. In all these cases carrying around the stack trace is useless and expensive.
It is, fortunately, easy to ask the JVM to stop gathering this information simply by overriding a method in the base Throwable class.
Here is the code for a "fast exception":
FastEx.java
public class FastEx extends Exception {
public Throwable fillInStackTrace()
{
return null;
}
}
When testing the fast exception vs normal exceptions I got almost an order-of-magnitude increase in speed!
$time java TestNormEx
real 0m40.262s
user 0m0.031s
sys 0m0.000s
$time java TestFastEx
real 0m4.976s
user 0m0.015s
sys 0m0.015s
The test code follows:
TestNormEx.java
public class TestNormEx
{
public void somethrow () throws Exception
{
throw new Exception ();
}
public void somefunc ()
{
for (int i = 0;i < 50000000;++i) {
try {
somethrow ();
}
catch (Exception e) {
// Do nothing
// since this is a pure timing test
}
}
}
public static void main (String args[])
{
TestNormEx t;
t = new TestNormEx ();
t.somefunc ();
}
}
TestFastEx.java
public class TestFastEx
{
public void somethrow () throws FastEx
{
throw new FastEx ();
}
public void somefunc ()
{
for (int i = 0;i < 50000000;++i) {
try {
somethrow ();
}
catch (FastEx e) {
// Do nothing
// since this is a pure timing test
}
}
}
public static void main (String args[])
{
TestFastEx t;
t = new TestFastEx ();
t.somefunc ();
}
}