Question: Is exception handling in Java actually slow?
Conventional wisdom, as well as a lot of Google results, says that exceptional logic shouldn\'t be used for n
Don't know if these topics relate, but I once wanted to implement one trick relying on current thread's stack trace: I wanted to discover the name of the method, which triggered the instantiation inside the instantiated class (yeap, the idea is crazy, I totally gave it up). So I discovered that calling Thread.currentThread().getStackTrace()
is extremely slow (due to native dumpThreads
method which it uses internally).
So Java Throwable
, correspondingly, has a native method fillInStackTrace
. I think that the killer-catch
block described earlier somehow triggers the execution of this method.
But let me tell you another story...
In Scala some functional features are compiled in JVM using ControlThrowable
, which extends Throwable
and overrides its fillInStackTrace
in a following way:
override def fillInStackTrace(): Throwable = this
So I adapted the test above (cycles amount are decreased by ten, my machine is a bit slower :):
class ControlException extends ControlThrowable
class T {
var value = 0
def reset = {
value = 0
}
def method1(i: Int) = {
value = ((value + i) / i) << 1
if ((i & 0xfffffff) == 1000000000) {
println("You'll never see this!")
}
}
def method2(i: Int) = {
value = ((value + i) / i) << 1
if ((i & 0xfffffff) == 1000000000) {
throw new Exception()
}
}
def method3(i: Int) = {
value = ((value + i) / i) << 1
if ((i & 0x1) == 1) {
throw new Exception()
}
}
def method4(i: Int) = {
value = ((value + i) / i) << 1
if ((i & 0x1) == 1) {
throw new ControlException()
}
}
}
class Main {
var l = System.currentTimeMillis
val t = new T
for (i <- 1 to 10000000)
t.method1(i)
l = System.currentTimeMillis - l
println("method1 took " + l + " ms, result was " + t.value)
t.reset
l = System.currentTimeMillis
for (i <- 1 to 10000000) try {
t.method2(i)
} catch {
case _ => println("You'll never see this")
}
l = System.currentTimeMillis - l
println("method2 took " + l + " ms, result was " + t.value)
t.reset
l = System.currentTimeMillis
for (i <- 1 to 10000000) try {
t.method4(i)
} catch {
case _ => // do nothing
}
l = System.currentTimeMillis - l
println("method4 took " + l + " ms, result was " + t.value)
t.reset
l = System.currentTimeMillis
for (i <- 1 to 10000000) try {
t.method3(i)
} catch {
case _ => // do nothing
}
l = System.currentTimeMillis - l
println("method3 took " + l + " ms, result was " + t.value)
}
So, the results are:
method1 took 146 ms, result was 2
method2 took 159 ms, result was 2
method4 took 1551 ms, result was 2
method3 took 42492 ms, result was 2
You see, the only difference between method3
and method4
is that they throw different kinds of exceptions. Yeap, method4
is still slower than method1
and method2
, but the difference is far more acceptable.