天天看點

啪啪,打臉了!上司說:try-catch必須放在循環體外!

哈喽,親愛的小夥伴們,技術學磊哥,進步沒得說!歡迎來到新一期的性能解讀系列,我是磊哥。

今天給大家帶來的是關于 try-catch 應該放在循環體外,還是放在循環體内的文章,我們将從性能和業務場景分析這兩個方面來回答此問題。

很多人對 try-catch 有一定的誤解,比如我們經常會把它(try-catch)和“低性能”直接畫上等号,但對 try-catch 的本質(是什麼)卻缺少着最基礎的了解,是以我們也會在本篇中對 try-catch 的本質進行相關的探索。

啪啪,打臉了!上司說:try-catch必須放在循環體外!
小貼士:我會盡量用代碼和評測結果來證明問題,但由于本身認知的局限,如有不當之處,請讀者朋友們在評論區指出。

話不多說,我們直接來開始今天的測試,本文我們依舊使用 Oracle 官方提供的 JMH(Java Microbenchmark Harness,JAVA 微基準測試套件)來進行測試。

首先在 pom.xml 檔案中添加 JMH 架構,配置如下:

完整測試代碼如下:

以上代碼的測試結果為:

啪啪,打臉了!上司說:try-catch必須放在循環體外!

從以上結果可以看出,程式在循環 1000 次的情況下,單次平均執行時間為:

循環内包含 try-catch 的平均執行時間是 635 納秒 ±75 納秒,也就是 635 納秒上下誤差是 75 納秒;

循環外包含 try-catch 的平均執行時間是 630 納秒,上下誤差 38 納秒。

也就是說,在沒有發生異常的情況下,除去誤內插補點,我們得到的結論是:try-catch 無論是在 <code>for</code> 循環内還是 <code>for</code> 循環外,它們的性能相同,幾乎沒有任何差别。

啪啪,打臉了!上司說:try-catch必須放在循環體外!

要了解 try-catch 的性能問題,必須從它的位元組碼開始分析,隻有這樣我能才能知道 try-catch 的本質到底是什麼,以及它是如何執行的。

此時我們寫一個最簡單的 try-catch 代碼:

然後使用 <code>javac</code> 生成位元組碼之後,再使用 <code>javap -c AppTest</code> 的指令來檢視位元組碼檔案:

從以上位元組碼中可以看到有一個異常表:

參數說明:

from:表示 try-catch 的開始位址;

to:表示 try-catch 的結束位址;

target:表示異常的處理起始位;

type:表示異常類名稱。

從位元組碼指令可以看出,當代碼運作時出錯時,會先判斷出錯資料是否在 <code>from</code> 到 <code>to</code> 的範圍内,如果是則從 <code>target</code> 标志位往下執行,如果沒有出錯,直接 <code>goto</code> 到 <code>return</code>。也就是說,如果代碼不出錯的話,性能幾乎是不受影響的,和正常的代碼的執行邏輯是一樣的。

啪啪,打臉了!上司說:try-catch必須放在循環體外!

雖然 try-catch 在循環體内還是循環體外的性能是類似的,但是它們所代碼的業務含義卻完全不同,例如以下代碼:

以上程式的執行結果為:

java.lang.Exception: new Exception at com.example.AppTest.innerForeach(AppTest.java:15) at com.example.AppTest.main(AppTest.java:5) at com.example.AppTest.outerForeach(AppTest.java:31) at com.example.AppTest.main(AppTest.java:6) 循環内的執行結果:5 循環外的執行結果:3

可以看出在循環體内的 try-catch 在發生異常之後,可以繼續執行循環;而循環外的 try-catch 在發生異常之後會終止循環。

是以我們在決定 try-catch 究竟是應該放在循環内還是循環外,不取決于性能(因為性能幾乎相同),而是應該取決于具體的業務場景。

例如我們需要處理一批資料,而無論這組資料中有哪一個資料有問題,都不能影響其他組的正常執行,此時我們可以把 try-catch 放置在循環體内;而當我們需要計算一組資料的合計值時,隻要有一組資料有誤,我們就需要終止執行,并抛出異常,此時我們需要将 try-catch 放置在循環體外來執行。

啪啪,打臉了!上司說:try-catch必須放在循環體外!

本文我們測試了 try-catch 放在循環體内和循環體外的性能,發現二者在循環很多次的情況下性能幾乎是一緻的。然後我們通過位元組碼分析,發現隻有當發生異常時,才會對比異常表進行異常處理,而正常情況下則可以忽略 try-catch 的執行。但在循環體内還是循環體外使用 try-catch,對于程式的執行結果來說是完全不同的,是以我們應該從實際的業務出發,來決定到 try-catch 應該存放的位置,而非性能考慮。

關注公衆号「Java中文社群」回複“幹貨”,擷取 50 篇原創幹貨 Top 榜。

關注下面二維碼,訂閱更多精彩内容。

啪啪,打臉了!上司說:try-catch必須放在循環體外!
啪啪,打臉了!上司說:try-catch必須放在循環體外!
啪啪,打臉了!上司說:try-catch必須放在循環體外!

關注公衆号(加好友):

啪啪,打臉了!上司說:try-catch必須放在循環體外!

作者:

王磊的部落格

出處:

http://vipstone.cnblogs.com/