<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          啪啪打臉!領(lǐng)導(dǎo)說:try-catch要放在循環(huán)體外!

          共 1577字,需瀏覽 4分鐘

           ·

          2020-10-29 08:12

          來源:Java中文社群? ? ?

          今天給大家?guī)淼氖顷P(guān)于 try-catch 應(yīng)該放在循環(huán)體外,還是放在循環(huán)體內(nèi)的文章,我們將從性能業(yè)務(wù)場景分析這兩個方面來回答此問題。

          很多人對 try-catch?有一定的誤解,比如我們經(jīng)常會把它(try-catch)和“低性能”直接畫上等號,但對 try-catch 的本質(zhì)(是什么)卻缺少著最基礎(chǔ)的了解,因此我們也會在本篇中對 try-catch 的本質(zhì)進(jìn)行相關(guān)的探索

          小貼士:我會盡量用代碼和評測結(jié)果來證明問題,但由于本身認(rèn)知的局限,如有不當(dāng)之處,請讀者朋友們在評論區(qū)指出。

          性能評測

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

          首先在 pom.xml 文件中添加 JMH 框架,配置如下:


          <dependency>
          ???<groupId>org.openjdk.jmhgroupId>
          ???<artifactId>jmh-coreartifactId>
          ???<version>{version}version>
          dependency>

          完整測試代碼如下:

          import?org.openjdk.jmh.annotations.*;
          import?org.openjdk.jmh.runner.Runner;
          import?org.openjdk.jmh.runner.RunnerException;
          import?org.openjdk.jmh.runner.options.Options;
          import?org.openjdk.jmh.runner.options.OptionsBuilder;

          import?java.util.concurrent.TimeUnit;

          /**
          ?*?try?-?catch?性能測試
          ?*/

          @BenchmarkMode(Mode.AverageTime)?//?測試完成時間
          @OutputTimeUnit(TimeUnit.NANOSECONDS)
          @Warmup(iterations?=?1,?time?=?1,?timeUnit?=?TimeUnit.SECONDS)?//?預(yù)熱?1?輪,每次?1s
          @Measurement(iterations?=?5,?time?=?5,?timeUnit?=?TimeUnit.SECONDS)?//?測試?5?輪,每次?3s
          @Fork(1)?//?fork?1?個線程
          @State(Scope.Benchmark)
          @Threads(100)
          public?class?TryCatchPerformanceTest?{
          ????private?static?final?int?forSize?=?1000;?//?循環(huán)次數(shù)
          ????public?static?void?main(String[]?args)?throws?RunnerException?{
          ????????//?啟動基準(zhǔn)測試
          ????????Options?opt?=?new?OptionsBuilder()
          ????????????????.include(TryCatchPerformanceTest.class.getSimpleName())?//?要導(dǎo)入的測試類
          ????????????????.build();
          ????????new?Runner(opt).run();?//?執(zhí)行測試
          ????}

          ????@Benchmark
          ????public?int?innerForeach()?{
          ????????int?count?=?0;
          ????????for?(int?i?=?0;?i?????????????try?{
          ????????????????if?(i?==?forSize)?{
          ????????????????????throw?new?Exception("new?Exception");
          ????????????????}
          ????????????????count++;
          ????????????}?catch?(Exception?e)?{
          ????????????????e.printStackTrace();
          ????????????}
          ????????}
          ????????return?count;
          ????}

          ????@Benchmark
          ????public?int?outerForeach()?{
          ????????int?count?=?0;
          ????????try?{
          ????????????for?(int?i?=?0;?i?????????????????if?(i?==?forSize)?{
          ????????????????????throw?new?Exception("new?Exception");
          ????????????????}
          ????????????????count++;
          ????????????}
          ????????}?catch?(Exception?e)?{
          ????????????e.printStackTrace();
          ????????}
          ????????return?count;
          ????}
          }

          以上代碼的測試結(jié)果為:


          從以上結(jié)果可以看出,程序在循環(huán) 1000 次的情況下,單次平均執(zhí)行時間為:

          • 循環(huán)內(nèi)包含 try-catch 的平均執(zhí)行時間是 635 納秒 ±75 納秒,也就是 635 納秒上下誤差是 75 納秒;
          • 循環(huán)外包含 try-catch 的平均執(zhí)行時間是 630 納秒,上下誤差 38 納秒。

          也就是說,在沒有發(fā)生異常的情況下,除去誤差值,我們得到的結(jié)論是:try-catch 無論是在 for?循環(huán)內(nèi)還是 ?for?循環(huán)外,它們的性能相同,幾乎沒有任何差別

          try-catch的本質(zhì)

          要理解 try-catch 的性能問題,必須從它的字節(jié)碼開始分析,只有這樣我能才能知道 try-catch 的本質(zhì)到底是什么,以及它是如何執(zhí)行的。

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

          public?class?AppTest?{
          ????public?static?void?main(String[]?args)?{
          ????????try?{
          ????????????int?count?=?0;
          ????????????throw?new?Exception("new?Exception");
          ????????}?catch?(Exception?e)?{
          ????????????e.printStackTrace();
          ????????}
          ????}
          }

          然后使用 javac?生成字節(jié)碼之后,再使用 javap -c AppTest?的命令來查看字節(jié)碼文件:

          ??javap?-c?AppTest?
          警告:?二進(jìn)制文件AppTest包含com.example.AppTest
          Compiled?from?"AppTest.java"
          public?class?com.example.AppTest?{
          ??public?com.example.AppTest();
          ????Code:
          ???????0:?aload_0
          ???????1:?invokespecial?#1??????????????????//?Method?java/lang/Object."":()V
          ???????4:?return

          ??public?static?void?main(java.lang.String[]);
          ????Code:
          ???????0:?iconst_0
          ???????1:?istore_1
          ???????2:?new???????????#2??????????????????//?class?java/lang/Exception
          ???????5:?dup
          ???????6:?ldc???????????#3??????????????????//?String?new?Exception
          ???????8:?invokespecial?#4??????????????????//?Method?java/lang/Exception."":(Ljava/lang/String;)V
          ??????11:?athrow
          ??????12:?astore_1
          ??????13:?aload_1
          ??????14:?invokevirtual?#5??????????????????//?Method?java/lang/Exception.printStackTrace:()V
          ??????17:?return
          ????Exception?table:
          ???????from????to??target?type
          ???????????0????12????12???Class?java/lang/Exception
          }

          從以上字節(jié)碼中可以看到有一個異常表:

          Exception?table:
          ???????from????to??target?type
          ??????????0????12????12???Class?java/lang/Exception

          參數(shù)說明:

          • from:表示 try-catch 的開始地址;
          • to:表示 try-catch 的結(jié)束地址;
          • target:表示異常的處理起始位;
          • type:表示異常類名稱。

          從字節(jié)碼指令可以看出,當(dāng)代碼運行時出錯時,會先判斷出錯數(shù)據(jù)是否在 from?到 to?的范圍內(nèi),如果是則從 target?標(biāo)志位往下執(zhí)行,如果沒有出錯,直接 goto?到 return。也就是說,如果代碼不出錯的話,性能幾乎是不受影響的,和正常的代碼的執(zhí)行邏輯是一樣的。

          業(yè)務(wù)情況分析

          雖然 try-catch 在循環(huán)體內(nèi)還是循環(huán)體外的性能是類似的,但是它們所代碼的業(yè)務(wù)含義卻完全不同,例如以下代碼:

          public?class?AppTest?{
          ????public?static?void?main(String[]?args)?{
          ????????System.out.println("循環(huán)內(nèi)的執(zhí)行結(jié)果:"?+?innerForeach());
          ????????System.out.println("循環(huán)外的執(zhí)行結(jié)果:"?+?outerForeach());
          ????}
          ????
          ????//?方法一
          ????public?static?int?innerForeach()?{
          ????????int?count?=?0;
          ????????for?(int?i?=?0;?i?6;?i++)?{
          ????????????try?{
          ????????????????if?(i?==?3)?{
          ????????????????????throw?new?Exception("new?Exception");
          ????????????????}
          ????????????????count++;
          ????????????}?catch?(Exception?e)?{
          ????????????????e.printStackTrace();
          ????????????}
          ????????}
          ????????return?count;
          ????}

          ????//?方法二
          ????public?static?int?outerForeach()?{
          ????????int?count?=?0;
          ????????try?{
          ????????????for?(int?i?=?0;?i?6;?i++)?{
          ????????????????if?(i?==?3)?{
          ????????????????????throw?new?Exception("new?Exception");
          ????????????????}
          ????????????????count++;
          ????????????}
          ????????}?catch?(Exception?e)?{
          ????????????e.printStackTrace();
          ????????}
          ????????return?count;
          ????}
          }

          以上程序的執(zhí)行結(jié)果為:

          java.lang.Exception: new Exception

          at com.example.AppTest.innerForeach(AppTest.java:15)

          at com.example.AppTest.main(AppTest.java:5)

          java.lang.Exception: new Exception

          at com.example.AppTest.outerForeach(AppTest.java:31)

          at com.example.AppTest.main(AppTest.java:6)

          循環(huán)內(nèi)的執(zhí)行結(jié)果:5

          循環(huán)外的執(zhí)行結(jié)果:3

          可以看出在循環(huán)體內(nèi)的 try-catch 在發(fā)生異常之后,可以繼續(xù)執(zhí)行循環(huán);而循環(huán)外的 try-catch 在發(fā)生異常之后會終止循環(huán)。

          因此我們在決定?try-catch 究竟是應(yīng)該放在循環(huán)內(nèi)還是循環(huán)外,不取決于性能(因為性能幾乎相同),而是應(yīng)該取決于具體的業(yè)務(wù)場景

          例如我們需要處理一批數(shù)據(jù),而無論這組數(shù)據(jù)中有哪一個數(shù)據(jù)有問題,都不能影響其他組的正常執(zhí)行,此時我們可以把 try-catch 放置在循環(huán)體內(nèi);而當(dāng)我們需要計算一組數(shù)據(jù)的合計值時,只要有一組數(shù)據(jù)有誤,我們就需要終止執(zhí)行,并拋出異常,此時我們需要將 try-catch 放置在循環(huán)體外來執(zhí)行。

          總結(jié)

          本文我們測試了 try-catch 放在循環(huán)體內(nèi)和循環(huán)體外的性能,發(fā)現(xiàn)二者在循環(huán)很多次的情況下性能幾乎是一致的。然后我們通過字節(jié)碼分析,發(fā)現(xiàn)只有當(dāng)發(fā)生異常時,才會對比異常表進(jìn)行異常處理,而正常情況下則可以忽略 try-catch 的執(zhí)行。但在循環(huán)體內(nèi)還是循環(huán)體外使用 try-catch,對于程序的執(zhí)行結(jié)果來說是完全不同的,因此我們應(yīng)該從實際的業(yè)務(wù)出發(fā),來決定到 try-catch 應(yīng)該存放的位置,而非性能考慮

          推薦閱讀

          介紹一款賊美的Vue+Element開源后臺管理UI

          騷操作:不重啟 JVM,如何替換掉已經(jīng)加載的類?

          放棄Spring Boot 中的 RestTemplate,我選擇 Retrofit !

          騰訊 Git 規(guī)范出爐,寫給開發(fā)者的指南!

          最棒 Spring Boot 干貨總結(jié)(超詳細(xì),建議收藏)

          我的天,Spring Boot 居然還有 Plus 版本

          瀏覽 36
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  波多野结衣无码NET,AV | 成人免费电影在线观看五月天婷婷 | 亚洲蜜桃一区二区 | 91午夜福利视频 | 中国精品一区二区 |