In Java, using throw/catch as a part of logic when there\'s not actually an error is generally a bad idea (in part) because throwing and catching an exception is expensive,
The creation of the Exception with a null stack trace takes about as much time as the throw and try-catch block together. However, filling the stack trace takes on average 5x longer.
I created the following benchmark to demonstrate the impact on performance. I added the -Djava.compiler=NONE to the Run Configuration to disable compiler optimization. To measure the impact of building the stack trace, I extended the Exception class to take advantage of the stack-free constructor:
class NoStackException extends Exception{
public NoStackException() {
super("",null,false,false);
}
}
The benchmark code is as follows:
public class ExceptionBenchmark {
private static final int NUM_TRIES = 100000;
public static void main(String[] args) {
long throwCatchTime = 0, newExceptionTime = 0, newObjectTime = 0, noStackExceptionTime = 0;
for (int i = 0; i < 30; i++) {
throwCatchTime += throwCatchLoop();
newExceptionTime += newExceptionLoop();
newObjectTime += newObjectLoop();
noStackExceptionTime += newNoStackExceptionLoop();
}
System.out.println("throwCatchTime = " + throwCatchTime / 30);
System.out.println("newExceptionTime = " + newExceptionTime / 30);
System.out.println("newStringTime = " + newObjectTime / 30);
System.out.println("noStackExceptionTime = " + noStackExceptionTime / 30);
}
private static long throwCatchLoop() {
Exception ex = new Exception(); //Instantiated here
long start = System.currentTimeMillis();
for (int i = 0; i < NUM_TRIES; i++) {
try {
throw ex; //repeatedly thrown
} catch (Exception e) {
// do nothing
}
}
long stop = System.currentTimeMillis();
return stop - start;
}
private static long newExceptionLoop() {
long start = System.currentTimeMillis();
for (int i = 0; i < NUM_TRIES; i++) {
Exception e = new Exception();
}
long stop = System.currentTimeMillis();
return stop - start;
}
private static long newObjectLoop() {
long start = System.currentTimeMillis();
for (int i = 0; i < NUM_TRIES; i++) {
Object o = new Object();
}
long stop = System.currentTimeMillis();
return stop - start;
}
private static long newNoStackExceptionLoop() {
long start = System.currentTimeMillis();
for (int i = 0; i < NUM_TRIES; i++) {
NoStackException e = new NoStackException();
}
long stop = System.currentTimeMillis();
return stop - start;
}
}
Output:
throwCatchTime = 19
newExceptionTime = 77
newObjectTime = 3
noStackExceptionTime = 15
This implies that creating a NoStackException is approximately as expensive as repeatedly throwing the same Exception. It also shows that creating an Exception and filling its stack trace takes approximately 4x longer.