1. 程式人生 > >從JVM視角分析try...catch...性能

從JVM視角分析try...catch...性能

comm har not 性能 start spa 不同 局部變量 var

隨便寫一個簡單的程序

public class Test {
    public static void main(String[] args) {
        int a = 0,b=2;
        try {
            a = b/0;
        }catch (Exception e){
            a = 1;
        }
    }
}

看一下字節碼指令過程:

public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=4, args_size=1
         0: iconst_0        push 0
         1: istore_1        pop並保存到局部變量1
         2: iconst_2       push 2
         3: istore_2       pop並保存到局部變量2
         4: iload_2        從局部變量裏拿出並push
         5: iconst_0        push 0 
         6: idiv          棧頂兩數相除
         7: istore_1
         8: goto          14
        11: astore_3
        12: iconst_1
        13: istore_1
        14: return
      Exception table:
         from    to  target type
             4     8    11   Class java/lang/Exception
      LineNumberTable:
        line 8: 0
        line 10: 4
        line 13: 8
        line 11: 11
        line 12: 12
        line 14: 14
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
           12       2     3     e   Ljava/lang/Exception;
            0      15     0  args   [Ljava/lang/String;
            2      13     1     a   I
            4      11     2     b   I
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 11
          locals = [ class "[Ljava/lang/String;", int, int ]
          stack = [ class java/lang/Exception ]
        frame_type = 2 /* same */
}
復制代碼

可以看到有個異常表:

Exception table:
         from    to  target type
             4     8    11   Class java/lang/Exception
復制代碼

from 表示 try catch 的開始地址 to 表示 try catch 的結束地址 target 表示異常的處理起始位 type 表示異常類名稱

代碼運行時出錯時,會先判斷出錯位置是否在 from - to 的範圍,如果是,則從 target 標誌位往下執行,如果沒有出錯,直接 gotoreturn 。可以看出,如果代碼不出錯的話,性能幾乎是不受影響的,和正常的代碼執行是一樣的。

那異常處理耗時是什麽個概念呢?

實驗二:異常處理耗時測試

public class Test {
    public static void main(String[] args) {
        int a = 0,b=2;
        long startTime = System.nanoTime();
        for (int i = 10; i>0;i--){
            try {
                a = b/i;
            }catch (Exception e){
                a = 1;
            }finally {

            }
        }
        long runTime = System.nanoTime()-startTime;
        System.out.println(runTime);
    }
}
復制代碼

我只需要把 i>0 改成 i>=0 ,程序遍會進行一次異常處理,因為除數不能為0.

我在修改之前(無異常運行),運行的結果是 1133 修改之後(會出現除數為0異常),運行結果是44177

當然,這個結果和cpu的算力有關,多次運行結果相差無幾。

所以,可以看出一旦程序進入到catch裏,是非常耗資源的。

那try catch 在for循環外面或者裏面,哪個更好呢?

實驗三:for循環在try裏面

public class Test {
    public static void main(String[] args) {
        int a = 0,b=2;
        long startTime = System.nanoTime();
        try {
            for (int i = 10; i>=0;i--){
                    a = b/i;
            }
        }catch (Exception e){
            a = 1;
        }finally {
            long runTime = System.nanoTime()-startTime;
            System.out.println(runTime);
        }
    }
}
復制代碼

運行多次的控制臺輸出:

46820     48708 54749  47953   46820  45310
復制代碼
public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=5, args_size=1
         0: iconst_0
         1: istore_1
         2: iconst_2
         3: istore_2
         4: bipush        10
         6: istore_3
         7: iload_3
         8: iflt          21
        11: iload_2
        12: iload_3
        13: idiv
        14: istore_1
        15: iinc          3, -1
        18: goto          7
        21: goto          35
        24: astore_3
        25: iconst_1
        26: istore_1
        27: goto          35
        30: astore        4
        32: aload         4
        34: athrow
        35: return
      Exception table:
         from    to  target type
             4    21    24   Class java/lang/Exception
             4    21    30   any
            24    27    30   any
            30    32    30   any
復制代碼

實驗四:try在for循環外面

public class Test {
    public static void main(String[] args) {
        int a = 0,b=2;
        long startTime = System.nanoTime();
        for (int i = 10; i>=0;i--){
            try {
                a = b/i;
            }catch (Exception e){
                a = 1;
            }finally {

            }
        }
                long runTime = System.nanoTime()-startTime;
                System.out.println(runTime);
    }
}
復制代碼

控制臺打印:

42289  47953  49463  45688  45310
復制代碼
public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=6, args_size=1
         0: iconst_0
         1: istore_1
         2: iconst_2
         3: istore_2
         4: bipush        10
         6: istore_3
         7: iload_3
         8: iflt          36
        11: iload_2
        12: iload_3
        13: idiv
        14: istore_1
        15: goto          30
        18: astore        4
        20: iconst_1
        21: istore_1
        22: goto          30
        25: astore        5
        27: aload         5
        29: athrow
        30: iinc          3, -1
        33: goto          7
        36: return
      Exception table:
         from    to  target type
            11    15    18   Class java/lang/Exception
            11    15    25   any
            18    22    25   any
            25    27    25   any
復制代碼

綜合實驗三和實驗四,我們發現無論從運行時長還是從字節碼指令的角度看,它兩的性能可以說是一樣的。並沒有你感覺到的for循環裏放try代碼會冗余、資源消耗加倍的問題。

但是從運行邏輯來看,兩個是有點不同的,實驗三中,因為for在try catch裏,所以jvm在編譯的時候,把異常處理放在for循環後面才進行。即: 第24-27行 ;實驗四中,異常處理是在for循環內部的,即: 第18-22行 。大同小異。

以上僅個人測試觀點,如果有誤請在下方留言,謝謝!

從JVM視角分析try...catch...性能