<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>

          面試官太難伺候?一個(gè)try-catch問(wèn)出這么多花樣

          共 3037字,需瀏覽 7分鐘

           ·

          2022-01-25 19:11

          哈嘍大家好,我是 Guide!

          剛剛面試回來(lái)的B哥又在吐槽了:現(xiàn)在的面試官太難伺候了,放著好好的堆、棧、方法區(qū)不問(wèn),上來(lái)就讓我從字節(jié)碼角度給他分析一下try-catch-finally(以下簡(jiǎn)稱(chēng)TCF)的執(zhí)行效率......

          我覺(jué)得應(yīng)該是面試官在面試的過(guò)程中看大家背的八股文都如出一轍,覺(jué)得沒(méi)有問(wèn)的必要,便拐著彎的考大家的理解。今天趁著B(niǎo)哥也在,我們就來(lái)好好總結(jié)一下TCF相關(guān)的知識(shí)點(diǎn),期待下次與面試官對(duì)線五五開(kāi)!

          環(huán)境準(zhǔn)備:IntelliJ IDEA 2020.2.3、JDK 1.8.0_181

          執(zhí)行順序

          我們先來(lái)寫(xiě)一段簡(jiǎn)單的代碼:

          public?static?int?test1()?{
          ????int?x?=?1;
          ????try?{
          ????????return?x;
          ????}?finally?{
          ????????x?=?2;
          ????}
          }

          答案是1不是2,你答對(duì)了嗎?

          大家都知道在TCF中,執(zhí)行到return的時(shí)候會(huì)先去執(zhí)行finally中的操作,然后才會(huì)返回來(lái)執(zhí)行return,那這里為啥會(huì)是1呢?我們來(lái)反編譯一下字節(jié)碼文件。

          命令:javap -v xxx.class

          cb7ffbb51b0974a0c6aa1d8207bfd7a4.webp

          字節(jié)碼指令晦澀難懂,那我們就用圖解的方式來(lái)解釋一下(我們先只看前7行指令):首先執(zhí)行 int x = 1;

          62fc845888484df99e9f3b1890d485ed.webp

          然后我們需要執(zhí)行try中的return x;

          4979725ff67b8813738fc3fb07ea4f38.webp

          此時(shí)并不是真正的返回x的值,而是將x的值存到局部變量表中作為臨時(shí)存儲(chǔ)變量進(jìn)行存儲(chǔ),也就是對(duì)該值進(jìn)行保護(hù)操作。

          最后進(jìn)入finally中執(zhí)行x=2;

          193c7166ed910fa70503f0d2b95b8a5e.webp

          此時(shí)雖然x已經(jīng)被賦值為2了,但是由于剛才的保護(hù)操作,在執(zhí)行真正的return操作時(shí),會(huì)將被保護(hù)的臨時(shí)存儲(chǔ)變量入棧返回。

          為了更好的理解上述操作,我們?cè)賮?lái)寫(xiě)一段簡(jiǎn)單代碼:

          public?static?int?test2()?{
          ????int?x?=?1;
          ????try?{
          ????????return?x;
          ????}?finally?{
          ????????x?=?2;
          ????????return?x;
          ????}
          }

          大家思考一下執(zhí)行結(jié)果是幾?答案是2不是1。

          我們?cè)賮?lái)看下該程序的字節(jié)碼指令

          52cadd7e0cfd6187b3b9900c5729696f.webp

          通過(guò)對(duì)比發(fā)現(xiàn),第6行一個(gè)是iload_1,一個(gè)是iload_0,這是由什么決定的呢?原因就是我們上邊提到的保護(hù)機(jī)制,當(dāng)在finally中存在return語(yǔ)句時(shí),保護(hù)機(jī)制便會(huì)失效,轉(zhuǎn)而將變量的值入棧并返回。

          小結(jié)

          • return的執(zhí)行優(yōu)先級(jí)高于finally的執(zhí)行優(yōu)先級(jí),但是return語(yǔ)句執(zhí)行完畢之后并不會(huì)馬上結(jié)束函數(shù),而是將結(jié)果保存到棧幀中的局部變量表中,然后繼續(xù)執(zhí)行finally塊中的語(yǔ)句;
          • 如果finally塊中包含return語(yǔ)句,則不會(huì)對(duì)try塊中要返回的值進(jìn)行保護(hù),而是直接跳到finally語(yǔ)句中執(zhí)行,并最后在finally語(yǔ)句中返回,返回值是在finally塊中改變之后的值;

          finally 為什么一定會(huì)執(zhí)行

          細(xì)心地小伙伴應(yīng)該能發(fā)現(xiàn),上邊的字節(jié)碼指令圖中第4-7行和第9-12行的字節(jié)碼指令是完全一致的,那么為什么會(huì)出現(xiàn)重復(fù)的指令呢?

          首先我們來(lái)分析一下這些重復(fù)的指令都做了些什么操作,經(jīng)過(guò)分析發(fā)現(xiàn)它們就是x = 2;return x;的字節(jié)碼指令,也就是finally代碼塊中的代碼。由此我們有理由懷疑如果上述代碼中加入catch代碼塊,finally代碼塊對(duì)應(yīng)的字節(jié)碼指令也會(huì)再次出現(xiàn)。

          public?static?int?test2()?{
          ????int?x?=?1;
          ????try?{
          ????????return?x;
          ????}?catch(Exception?e)?{
          ????????x?=?3;
          ????}?finally?{
          ????????x?=?2;
          ????????return?x;
          ????}
          }

          反編譯之后

          73dd2760be500d0590d160f173fd78a7.webp

          果然如我們所料,重復(fù)的字節(jié)碼指令出現(xiàn)了三次。讓我們回歸到最初的問(wèn)題上,為什么finally代碼的字節(jié)碼指令會(huì)重復(fù)出現(xiàn)三次呢?

          原來(lái)是JVM為了保證所有異常路徑和正常路徑的執(zhí)行流程都要執(zhí)行finally中的代碼,所以在trycatch后追加上了finally中的字節(jié)碼指令,再加上它自己本身的指令,正好三次。這也就是為什么finally 一定會(huì)執(zhí)行的原因。

          finally一定會(huì)執(zhí)行嗎?

          為什么上邊已經(jīng)說(shuō)了finally中的代碼一定會(huì)執(zhí)行,現(xiàn)在還要再多此一舉呢?請(qǐng)??看

          在正常情況下,它是一定會(huì)被執(zhí)行的,但是至少存在以下三種情況,是一定不執(zhí)行的:

          • try語(yǔ)句沒(méi)有被執(zhí)行到就返回了,這樣finally語(yǔ)句就不會(huì)執(zhí)行,這也說(shuō)明了finally語(yǔ)句被執(zhí)行的必要而非充分條件是:相應(yīng)的try語(yǔ)句一定被執(zhí)行到;
          • try代碼塊中有System.exit(0);這樣的語(yǔ)句,因?yàn)?code style="font-size:14px;background-color:rgba(27,31,35,.05);font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(239,112,96);">System.exit(0);是終止JVM的,連JVM都停止了,finally肯定不會(huì)被執(zhí)行了;
          • 守護(hù)線程會(huì)隨著所有非守護(hù)線程的退出而退出,當(dāng)守護(hù)線程內(nèi)部的finally的代碼還未被執(zhí)行到,非守護(hù)線程終結(jié)或退出時(shí),finally 肯定不會(huì)被執(zhí)行;

          TCF 的效率問(wèn)題

          說(shuō)起TCF的效率問(wèn)題,我們不得不介紹一下異常表,拿上邊的程序來(lái)說(shuō),反編譯class文件后的異常表信息如下:

          3107d7d98cbf7e376e714ddceb23c110.webp
          • from:代表異常處理器所監(jiān)控范圍的起始位置;
          • to:代表異常處理器所監(jiān)控范圍的結(jié)束位置(該行不被包括在監(jiān)控范圍內(nèi),是前閉后開(kāi)區(qū)間);
          • target:指向異常處理器的起始位置;
          • type:代表異常處理器所捕獲的異常類(lèi)型;

          圖中每一行代表一個(gè)異常處理器

          工作流程:

          1. 觸發(fā)異常時(shí),JVM會(huì)從上到下遍歷異常表中所有的條目;
          2. 比較觸發(fā)異常的行數(shù)是否在from-to范圍內(nèi);
          3. 范圍匹配之后,會(huì)繼續(xù)比較拋出的異常類(lèi)型和異常處理器所捕獲的異常類(lèi)型type是否相同;
          4. 如果類(lèi)型相同,會(huì)跳轉(zhuǎn)到target所指向的行數(shù)開(kāi)始執(zhí)行;
          5. 如果類(lèi)型不同,會(huì)彈出當(dāng)前方法對(duì)應(yīng)的java棧幀,并對(duì)調(diào)用者重復(fù)操作;
          6. 最壞的情況下JVM需要遍歷該線程 Java 棧上所有方法的異常表;

          拿第一行為例:如果位于2-4行之間的命令(即try塊中的代碼)拋出了Class java/lang/Exception類(lèi)型的異常,則跳轉(zhuǎn)到第8行開(kāi)始執(zhí)行。

          8: astore_1是指將拋出的異常對(duì)象保存到局部變量表中的1位置處

          從字節(jié)碼指令的角度來(lái)講,如果代碼中沒(méi)有異常拋出,TCF的執(zhí)行時(shí)間可以忽略不計(jì);如果代碼執(zhí)行過(guò)程中出現(xiàn)了上文中的第6條,那么隨著異常表的遍歷,更多的異常實(shí)例被構(gòu)建出來(lái),異常所需要的棧軌跡也在生成。該操作會(huì)逐一訪問(wèn)當(dāng)前線程的棧幀,記錄各種調(diào)試信息,包括類(lèi)名、方法名、觸發(fā)異常的代碼行數(shù)等等。所以執(zhí)行效率會(huì)大大降低。

          ··········? END? ··············
          也許你還想看??|?官宣!我升級(jí)了!!!??|?IntelliJ IDEA 老司機(jī),還沒(méi)用過(guò)這 5 個(gè) Intellij IDEA 調(diào)試魔法???|?用 Java 寫(xiě)個(gè)沙盒塔防游戲!已上架 Steam,Apple Store??|?干掉 CRUD!這個(gè)開(kāi)發(fā)神器效率爆炸,功能強(qiáng)大? |?Java 大殺器來(lái)了!性能提升一個(gè)數(shù)量級(jí)? |?豆瓣 9.7!這本技術(shù)書(shū)籍直接封神了? |?兩天兩夜,1M圖片優(yōu)化到100kb!? |?面試八股文,YYDS!

          簡(jiǎn)歷指導(dǎo)/Java 學(xué)習(xí)/面試指導(dǎo)/面試小冊(cè),歡迎加入我的知識(shí)星球(公眾號(hào)后臺(tái)回復(fù)“星球”即可)。

          如果本文對(duì)你有幫助的話,歡迎點(diǎn)贊&在看&分享,這對(duì)我繼續(xù)分享&創(chuàng)作優(yōu)質(zhì)文章非常重要。感謝????

          瀏覽 21
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  乱伦毛片AV | 在线播放你懂的 | 免费亚洲高清视频 | 一区二区三区无马亚 | 成人精品秘 久久久按摩下载 |