<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    LetsCoding.cn

    天地之間有桿秤,拿秤砣砸老百姓。

    JVM中finally子句介紹

    歡迎來到“Under The Hood”第七期。本期我們介紹JVM處理finally子句的方式及相關(guān)字節(jié)碼。你可能需要閱讀往期的文章才能更好的理解本文。

    finally子句

    JVM執(zhí)行Java字節(jié)碼時(shí),它有幾種方式可以退出一個(gè)代碼塊(花括號中間的語句)。其中之一,就是簡單的執(zhí)行完其中所有的語句,然后退出代碼塊。第二種,JVM可能會在代碼塊中間的任何一處,遇到像break,continue,return之類的語句,強(qiáng)制它跳出該代碼塊。第三種,JVM可能會在執(zhí)行過程中,出現(xiàn)了異常,然后它跳轉(zhuǎn)到匹配的catch子句,或者沒有找到相應(yīng)的catch子句,直接退出當(dāng)前線程。由于單個(gè)代碼塊有如此多的潛在退出點(diǎn)(exit point),擁有一個(gè)簡單的方式來表達(dá)“無論代碼塊以什么方式退出,有些事情總能發(fā)生”是很值得的。然后就有了try-finally子句。

    try-finally子句的用法:

    • 把擁有多個(gè)退出點(diǎn)的代碼塊放在try塊中,并且
    • 把無論try塊怎么退出,始終能被執(zhí)行的代碼放在finally塊中。

    例如:

    1. try {
    2.     // Block of code with multiple exit points
    3. }
    4. finally {
    5.     // Block of code that is always executed when the try block is exited,
    6.     // no matter how the try block is exited
    7. }

    如果try塊有多個(gè)catch子句與之關(guān)聯(lián),你就必須把finally子句放在所有catch子句的后面:

    1. try {
    2.     // Block of code with multiple exit points
    3. }
    4. catch (Cold e) {
    5.     System.out.println("Caught cold!");
    6. }
    7. catch (APopFly e) {
    8.     System.out.println("Caught a pop fly!");
    9. }
    10. catch (SomeonesEye e) {
    11.     System.out.println("Caught someone's eye!");
    12. }
    13. finally {
    14.     // Block of code that is always executed when the try block is exited,
    15.     // no matter how the try block is exited.
    16.     System.out.println("Is that something to cheer about?");
    17. }

    在try塊的代碼執(zhí)行過程中,先由catch子句負(fù)責(zé)處理拋出的異常,然后再執(zhí)行finall子句中的代碼。例如,如果上述代碼中的try塊拋出Cold異常,控制臺將會輸出如下信息:

    1. Caught cold!
    2. Is that something to cheer about?

    字節(jié)碼中的try-finally子句

    在字節(jié)碼中,finally子句扮演著方法中子程序的角色。在try塊和它所關(guān)聯(lián)的catch子句中的每個(gè)退出點(diǎn),代表finally子句的子程序會被調(diào)用。一旦最后一條語句執(zhí)行完成,且沒有拋出異常,沒有執(zhí)行return、continue、和break,則finally子句調(diào)用結(jié)束,子程序返回。JVM從調(diào)用子程序的指令后下一條指令繼續(xù)執(zhí)行,這樣try塊就能以適當(dāng)?shù)姆绞酵顺隽恕?/p>

    讓JVM跳轉(zhuǎn)到子程序的操作碼是jsr指令。jsr指令有2個(gè)單字節(jié)操作數(shù),它們組成子程序入口地址到j(luò)sr跳轉(zhuǎn)指令的偏移量。jsr_w指令是jsr的變種,功能和jsr相同,但有4個(gè)單字節(jié)操作數(shù)。當(dāng)JVM遇到j(luò)sr或jsr_w指令,它把返回地址(return address,即jsr或jsr_w指令的下一條指令地址)壓入棧中,然后從子程序的入口處繼續(xù)往下執(zhí)行。

    JVM在子程序完成之后,調(diào)用ret指令,從子程序返回。ret指令擁有一個(gè)操作數(shù),它是一個(gè)索引,指向存儲返回地址的本地變量。處理finally子句的操作碼總結(jié)如下:

    OPCODE
    OPERAND(S)
    DESCRIPTION
    jsr branchbyte1, branchbyte2 pushes the return address, branches to offset
    jsr_w branchbyte1, branchbyte2, branchbyte3, branchbyte4 pushes the return address, branches to wide offset
    ret index returns to the address stored in local variable index

    不要把子程序和Java里的方法搞混了,Java中的方法會使用不同于子程序的指令集。invokevirtual或invokeonvirtual指令用來處理方法調(diào)用,而return,areturn或ireturn指令用來處理方法返回。jsr指令不會導(dǎo)致方法被調(diào)用,而會使JVM跳轉(zhuǎn)到同一方法中不同的指令處。類似的,ret指令不會從方法中返回,它讓JVM從子程序返回到j(luò)sr指令的下一條指令處。實(shí)現(xiàn)finally子句的字節(jié)碼之所以被稱為子程序,是因?yàn)樗鼈兛雌饋硐袷菃蝹€(gè)方法的字節(jié)碼流中的很小的子程序。

    你可能會認(rèn)為ret指令應(yīng)該把返回地址從棧中彈出,因?yàn)槟抢锸撬籮sr指令壓入的地方。但是,你錯(cuò)了。

    每個(gè)子程序的開始,返回地址就被從棧頂彈出,并保存到本地變量中。ret指令會從同一個(gè)本地變量獲得子程序的返回地址。這種不對稱的返回地址使用方式是必須的,因?yàn)閒inall子句(子程序)本身可以拋出異常,或者包含return,break或continue語句。由于這種可能性,被jsr指令壓入棧中的返回地址必須立即從棧頂移除,這樣當(dāng)JVM由于break,continue,return語句或拋出的異常而從finally子句中退出時(shí),返回地址就不會依舊保存在棧中。因此,返回地址在子程序執(zhí)行的開始,就被保存到本地變量中。

    作為示例,參考下面的代碼,它包含一個(gè)擁有break語句的finally子句。無論傳給surpriseTheProgrammer()方法的參數(shù)是什么,這段代碼的結(jié)果總是false。

    1. static boolean surpriseTheProgrammer(boolean bVal) {
    2.     while (bVal) {
    3.         try {
    4.             return true;
    5.         }
    6.         finally {
    7.             break;
    8.         }
    9.     }
    10.     return false;
    11. }

    上面的例子顯示了,為什么要在子程序的開始處,就把返回地址保存到本地變量中。因?yàn)閒inally子句從break返回,它不會執(zhí)行ret指令。結(jié)果就是JVM不會執(zhí)行“return true”語句,它會執(zhí)行break語句,并繼續(xù)往下執(zhí)行,結(jié)束while循環(huán),執(zhí)行“return false”。

    以break語句退出finally子句的方式,跟以return,continue或者拋出異常的方式退出是一樣的。如果finally子句以這四種方式之一退出,子句中的ret指令永遠(yuǎn)都不會被執(zhí)行到。鑒于此,ret指令不能保證肯定會被執(zhí)行,JVM不能指望它去移除棧中的返回地址。因此,返回地址在子程序執(zhí)行的開始,就被保存到本地變量中。

    作為一個(gè)完整的例子,請看如下方法,它包含一個(gè)有2個(gè)退出點(diǎn)的try塊。

    1. static int giveMeThatOldFashionedBoolean(boolean bVal) {
    2.     try {
    3.         if (bVal) {
    4.             return 1;
    5.         }
    6.         return 0;
    7.     }
    8.     finally {
    9.         System.out.println("Got old fashioned.");
    10.     }
    11. }

    它的字節(jié)碼如下:

    1. // The bytecode sequence for the try block:
    2. 0 iload_0               // Push local variable 0 (arg passed as divisor)
    3. 1 ifeq 11               // Push local variable 1 (arg passed as dividend)
    4. 4 iconst_1              // Push int 1
    5. 5 istore_3              // Pop an int (the 1), store into local variable 3
    6. 6 jsr 24                // Jump to the mini-subroutine for the finally clause
    7. 9 iload_3               // Push local variable 3 (the 1)
    8. 10 ireturn               // Return int on top of the stack (the 1)
    9. 11 iconst_0              // Push int 0
    10. 12 istore_3              // Pop an int (the 0), store into local variable 3
    11. 13 jsr 24                // Jump to the mini-subroutine for the finally clause
    12. 16 iload_3               // Push local variable 3 (the 0)
    13. 17 ireturn               // Return int on top of the stack (the 0)
    14. // The bytecode sequence for a catch clause that catches any kind of exception
    15. // thrown from within the try block.
    16. 18 astore_1              // Pop the reference to the thrown exception, store
    17.                          // into local variable 1
    18. 19 jsr 24                // Jump to the mini-subroutine for the finally clause
    19. 22 aload_1               // Push the reference (to the thrown exception) from
    20.                            // local variable 1
    21. 23 athrow                // Rethrow the same exception
    22. // The miniature subroutine that implements the finally block.
    23. 24 astore_2              // Pop the return address, store it in local variable 2
    24. 25 getstatic #8          // Get a reference to java.lang.System.out
    25. 28 ldc #1                // Push < string "Got old fashioned." > from the constant pool
    26. 30 invokevirtual #7      // Invoke System.out.println()
    27. 33 ret 2                 // Return to return address stored in local variable 2

    try塊的字節(jié)碼中含有2個(gè)jsr指令,另外一個(gè)jsr指令在catch子句中。catch子句是由編譯器自動(dòng)添加的,因?yàn)槿绻趖ry塊執(zhí)行過程中拋出異常,finally塊必須任然被執(zhí)行。因此catch子句僅僅調(diào)用代表finally塊的子程序,然后拋出相同的異常。下面所示的giveMeThatOldFashionedBoolean() 方法的異常表說明,0到17行所拋出的任何異常,都由從18行開始的catch子句來處理。

    FROM
    TO
    TARGET
    TYPE
    0 18 18 any

    可以看出,finally子句的字節(jié)碼,以彈出并保存棧頂返回地址到本地變量開始,以從本地變量2中取得返回地址的ret指令返回。

    本文譯自:Try-finally clauses defined and demonstrated

    譯者注:基于字節(jié)碼校驗(yàn)方面的考量,JVM中的jsr/ret指令在Java 6.0時(shí),已經(jīng)被移除。

    本文出自:碼農(nóng)合作社 》JVM中finally子句介紹,轉(zhuǎn)載請注明。

    posted on 2014-06-08 02:42 Rolandz 閱讀(2180) 評論(1)  編輯  收藏 所屬分類: 編程實(shí)踐

    評論

    # re: JVM中finally子句介紹 2014-06-14 12:23 泡菜

    好好學(xué)習(xí),一定能成功的!相信自己,相信一定有發(fā)展的。  回復(fù)  更多評論   

    導(dǎo)航

    統(tǒng)計(jì)

    留言簿(1)

    隨筆分類(12)

    隨筆檔案(19)

    積分與排名

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 亚洲一级毛片免费看| 亚洲精品无码av人在线观看| 免费人成网站在线观看不卡| 亚洲av无码兔费综合| 亚洲电影在线免费观看| 亚洲情XO亚洲色XO无码| 男人的天堂亚洲一区二区三区 | 牛牛在线精品观看免费正| 亚洲videosbestsex日本| 亚洲国产天堂久久综合网站| 美腿丝袜亚洲综合| 免费在线一级毛片| 女人18毛片a级毛片免费视频| 88xx成人永久免费观看| a毛片视频免费观看影院| 精品国产免费人成网站| 一级成人a做片免费| 黄页网址在线免费观看| 久久亚洲中文无码咪咪爱| 亚洲色大成网站www久久九| 亚洲三级高清免费| 亚洲AV一二三区成人影片| 91亚洲国产成人久久精品| 亚洲精品日韩中文字幕久久久| 亚洲成人在线网站| 日本亚洲欧洲免费天堂午夜看片女人员 | 国产成人青青热久免费精品| 香蕉视频在线观看免费国产婷婷 | 亚洲乱码在线卡一卡二卡新区| 亚洲视频在线观看网站| 亚洲综合男人的天堂色婷婷| 久久精品国产亚洲AV嫖农村妇女| 久久青草亚洲AV无码麻豆| 亚洲αv在线精品糸列| 亚洲国产综合专区在线电影| 久久亚洲AV无码精品色午夜麻| 亚洲国产综合专区在线电影 | 99re这里有免费视频精品| 69式互添免费视频| 在线观看免费高清视频| 在线免费视频一区|