Ở bài Clean Code with Exception mình có hứa sẽ giải quyết vấn đề làm Exception
chậm. Vì thế bài này sẽ đi vào sâu hơn việc Java sẽ làm gì khi có 1 Exception
được throw ra và giải thích stack trace
là gì… và từ đó sẽ tối ưu việc sử dụng Exception
1. Java sẽ làm gì khi có 1 Exception được throw ra?
Để hiểu rõ Java làm gì khi có 1 Exception
được throw ra thì ta hãy vào xem code của class Throwable
cha của Exception
hen ;))
|
|
Đoạn code trên cho ta thấy tất cả các public constructor
của Throwable
đều gọi hàm fillInStackTrace()
. Vậy hàm này làm gì và tại sao cần gọi nó? Để có được câu trả lời ta đi vào phần tiếp theo
2. Stack trace là gì? Vì sao cần dùng nó?
Giả sử ta có đoạn code sau và thử chạy nó
|
|
Kết quả sẽ có lỗi sau:
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
Phân tích message lỗi trên ta có diễn dịch lại sau:
- Có exception
HighLevelException
được throw ra. Nguyên nhân củaHighLevelException
làMidLevelException
và nguyên nhân củaMidLevelException
làLowLevelException
HighLevelException
xuất hiện ở classJunk
methoda
fileJunk.java
dòng thứ 13 và methoda
được gọi từ classJunk
methodmain
fileJunk.java
dòng thứ 4MidLevelException
xuất hiện ở classJunk
methodc
fileJunk.java
dòng thứ 23, methodc
được gọi từ classJunk
methodb
fileJunk.java
dòng thứ 17 và methodb
được gọi từ classJunk
methoda
fileJunk.java
dòng thứ 11LowLevelException
thì tương tự như vớiMidLevelException
vàHighLevelException
chứ mình cũng hơi đau đầu rồi đó. Tất nhiên nếu có lỗi và cần trace thì vẫn phải làm ;))
Và từ phân tích trên ta có thể vẻ lại flow mà exception bị throw ra như thế nào:
Hình vẻ trên của mình cũng có thể coi là một biểu diễn của stack trace
. Nói rõ hơn stack trace
là một danh sách có thứ tự của các lời gọi hàm và được sắp xếp theo thứ tự gọi từ các lời gọi gần đây nhất tới lời gọi đầu tiên. Nó rất hữu ích trong việc phân tích, tìm nguyên nhân và từ đó ra quyết định có cần fix lỗi hay không.
Quay lại câu hỏi ở #1. Thì ta đã có câu trả lời. Hàm fillInStackTrace()
là đi collect stack trace
và nó rất hữu ích phải không nào 🤟
3. Collect stack trace nhanh hay chậm?
Câu trả lời là rất chậm và để biết chậm như thế nào bạn có thể clone repo này. Còn nếu bạn làm biến thì xem #5 nha.
4. Tắt collect stack trace và optimize
Mình có viết lại NSTException
và đơn giản hoá 2 hàm getStackTrace()
và fillInStackTrace()
như đoạn code sau:
|
|
Lúc này khi NSTException
được khởi tạo nó sẽ không collect stack trace
nữa. Cùng xem thành quả như thế nào nhé.
5. Benchmark
Mình giải thích chút xíu về cách thức benchmark. Mình có 3 method callNotThrowException
, callToThrowException
và callToThrowNSTException
dùng giả lập các tình huống:
callNotThrowException
: tình huống bình thường, không cóException
này được throw racallToThrowException
: tình huốngException
được throw racallToThrowNSTException
: tình huốngNSTException
được throw ra.NSTException
viết tắt của No Stack Trace Exception là một custom exception mình viết lại, tắt collection stack trace để tăng hiệu năng
Do khác nhau về cấu hình, OS, Java version… nên kết quả có thể khác nhau. Và đây là kết quả từ máy mình:
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
Ta chú ý 2 kết quả của Jmh.callNotThrowException
và Jmh.callToThrowException
lần lượt là 80504640.048
và 703653.862
(bỏ qua sai số ±) thì 80504640.048 / 703653.862 = 114 quả là một con số rất lớn.
Kết luận: bằng việc sử dụng NSTException
để tắt collect stack trace
giúp ta có những ưu điểm sau:
- Tăng tốc ứng dụng và sử dụng bộ nhớ hiệu quả hơn
- Làm cho method signature dễ hiểu
- Code control bên ngoài dễ hiểu hơn khi tách biệt code bussiness và handle lỗi, mình cũng sẽ có bài về phần này
Nhưng kèm đó cũng có nhược điểm là không còn stack trace, từ đó việc trace lỗi sẽ khó khăn. Mời các bạn đón xem bài tiếp theo để giải quyết.