In the post Clean Code with Exception I promised to solve the problem of making Exception
slow. So this post will go deeper into what Java will do when an Exception
is thrown and explain what a stack trace
is… and from there will optimize the use of Exception
1. What will Java do when an Exception is thrown?
To clearly understand what Java does when an Exception
is thrown, let’s look at the code of the Throwable
class, the parent of Exception
;))
|
|
The above code shows us that all the public constructors
of Throwable
call the fillInStackTrace()
function. So what does this function do and why do we need to call it? To get the answer, let’s go to the next section
2. What is a stack trace? Why do we need to use it?
Suppose we have the following code and try to run it
|
|
The result will be the following error:
HighLevelException: MidLevelException: LowLevelException
at Junk.a(Junk.java:13)
at Junk.main(Junk.java:4)
Caused by: MidLevelException: LowLevelException
at Junk.c(Junk.java:23)
at Junk.b(Junk.java:17)
at Junk.a(Junk.java:11)
... 1 more
Caused by: LowLevelException
at Junk.e(Junk.java:30)
at Junk.d(Junk.java:27)
at Junk.c(Junk.java:21)
... 3 more
Analyzing the above error message, we have the following interpretation:
- An exception
HighLevelException
was thrown. The cause ofHighLevelException
isMidLevelException
and the cause ofMidLevelException
isLowLevelException
HighLevelException
appears in classJunk
methoda
fileJunk.java
line 13 and methoda
is called from classJunk
methodmain
fileJunk.java
line 4MidLevelException
appears in classJunk
methodc
fileJunk.java
line 23, methodc
is called from classJunk
methodb
fileJunk.java
line 17 and methodb
is called from classJunk
methoda
fileJunk.java
line 11LowLevelException
is similar toMidLevelException
andHighLevelException
but I also have a headache. Of course, if there is an error and need to trace, I still have to do it ;))
And from the above analysis, we can redraw the flow where the exception is thrown how it looks like:
My image above can also be considered a representation of stack trace
. To be more specific, stack trace
is an ordered list of function calls and is arranged in the order of calls from the most recent to the first call. It is very useful in analyzing, finding the cause and from there deciding whether to fix the error or not.
Back to the question in #1. Then we have the answer. The fillInStackTrace()
function is to collect stack trace
and it is very useful, isn’t it 🤟
3. Collect stack trace fast or slow?
The answer is very slow and to see how slow you can clone this repo. If you do variables, see #5.
4. Disable collect stack trace and optimize
I rewrote NSTException
and simplified the 2 functions getStackTrace()
and fillInStackTrace()
as the following code:
|
|
Now when NSTException
is initialized, it will not collect stack trace
anymore. Let’s see the results.
5. Benchmark
I will explain a little bit about how to benchmark. I have 3 methods callNotThrowException
, callToThrowException
and callToThrowNSTException
used to simulate the following situations:
callNotThrowException
: normal situation, noException
is throwncallToThrowException
: situation whereException
is throwncallToThrowNSTException
: situation whereNSTException
is thrown.NSTException
stands for No Stack Trace Exception, a custom exception I rewrote, turning off stack trace collection to increase performance
Due to differences in configuration, OS, Java version… the results may vary. And here are the results from my machine:
Benchmark (param) Mode Cnt Score Error Units
Jmh.callNotThrowException 1 thrpt 25 80504640.048 ± 15390305.644 ops/s
Jmh.callToThrowException 1 thrpt 25 703653.862 ± 58119.745 ops/s
Jmh.callToThrowNSTException 1 thrpt 25 41387559.456 ± 6619635.038 ops/s
We note that the 2 results of Jmh.callNotThrowException
and Jmh.callToThrowException
are 80504640.048
and 703653.862
(ignoring the ± error) then 80504640.048 / 703653.862 = 114 is a very large number.
Conclusion: By using NSTException
to turn off collect stack trace
, we have the following advantages:
- Speed up the application and use memory more efficiently
- Make the method signature easier to understand
- External control code is easier to understand when separating business code and error handling, I will also have a post about this part
But there is also a disadvantage that there is no stack trace, from which tracing errors will be difficult. Please watch the next post to solve it.