异常机制

发布时间 2023-12-06 15:38:01作者: anpeiyong

JVM如何处理异常?

exception table

概念

  方法的异常表

    from :可能发生异常的起始点

    to :可能发生异常的结束点

    target :上述from和to之前发生异常后的异常处理者的位置

    type :异常处理者处理的异常的类信息

什么时候被使用?

  异常发生的时候

如何被使用?

  1.JVM会在当前出现异常的方法中,查找异常表,是否有合适的处理者来处理

  2.如果当前方法异常表不为空,并且异常符合处理者的from和to节点,并且type也匹配,则JVM调用位于target的调用者来处理。

  3.如果上一条未找到合理的处理者,则继续查找异常表中的剩余条目

  4.如果当前方法的异常表无法处理,则向上查找(弹栈处理)刚刚调用该方法的调用处,并重复上面的操作。

  5.如果所有的栈帧被弹出,仍然没有处理,则抛给当前的Thread,Thread则会终止。

  6.如果当前Thread为最后一个非守护线程,且未处理异常,则会导致JVM终止运行。

try...catch示例

public static void simpleTryCatch() {
        try {
            testNPE();
        } catch (Exception e) {
            e.getMessage();
        }
    }



字节码:

public static void simpleTryCatch();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=1, args_size=0
         0: invokestatic  #3                  // Method testNPE:()V
         3: goto          12
         6: astore_0
         7: aload_0
         8: invokevirtual #5                  // Method java/lang/Exception.getMessage:()Ljava/lang/String;
        11: pop
        12: return
      Exception table:
         from    to  target type
             0     3     6   Class java/lang/Exception

  

try...catch...finally示例

public static void simpleTryCatch() {
        try {
            testNPE();
        } catch (Exception e) {
            e.getMessage();
        }finally {
            System.out.println("finally");
        }
    }


字节码:

public static void simpleTryCatch();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=0
         0: invokestatic  #3                  // Method testNPE:()V
         3: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
         6: ldc           #5                  // String finally
         8: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        11: goto          42
        14: astore_0
        15: aload_0
        16: invokevirtual #8                  // Method java/lang/Exception.getMessage:()Ljava/lang/String;
        19: pop
        20: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        23: ldc           #5                  // String finally
        25: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        28: goto          42
        31: astore_1
        32: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        35: ldc           #5                  // String finally
        37: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        40: aload_1
        41: athrow
        42: return
      Exception table:
         from    to  target type
             0     3    14   Class java/lang/Exception
             0     3    31   any
            14    20    31   any

  

  异常表中,有三条数据,而我们仅仅捕获了一个Exception, 异常表的后两个item的type为any; 上面的三条异常表item的意思为:

    如果0到3之间,发生了Exception类型的异常,调用14位置的异常处理者。

    如果0到3之间,无论发生什么异常,都调用31位置的处理者

    如果14到20之间(即catch部分),不论发生什么异常,都调用31位置的处理者。

finally为什么总是被执行?

  finally逻辑 被提取到 try、catch位置

public static void simpleTryCatch() {
        try {
            testNPE();
        } catch (Exception e) {
            e.getMessage();
        }finally {
            System.out.println("finally");
        }
    }

  

public static void simpleTryCatch();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=0
         0: invokestatic  #3                  // Method testNPE:()V
         3: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
         6: ldc           #5                  // String finally
         8: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        11: goto          42   // try提取finally逻辑,如果无异常,goto 42,执行结束
        14: astore_0
        15: aload_0
        16: invokevirtual #8                  // Method java/lang/Exception.getMessage:()Ljava/lang/String;
        19: pop
        20: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        23: ldc           #5                  // String finally
        25: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        28: goto          42  // catch提取finally逻辑,如果无异常,goto 42,执行结束
        31: astore_1
        32: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        35: ldc           #5                  // String finally
        37: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        40: aload_1
        41: athrow    // 如果发生异常,未被处理,执行完finally逻辑,将异常抛给调用方
        42: return
      Exception table:
         from    to  target type
             0     3    14   Class java/lang/Exception
             0     3    31   any
            14    20    31   any

  

return和finally的问题

在try、catch中使用return

  finally都会正常被执行

public static String simpleTryCatch() {
        try {
            testNPE();
            return "ok";
        } catch (Exception e) {
            e.getMessage();
            return "catch";
        }finally {
            System.out.println("finally");
        }
    }


字节码:

 public static java.lang.String simpleTryCatch();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=0
         0: invokestatic  #5                  // Method testNPE:()V
         3: ldc           #6                  // String ok
         5: astore_0
         6: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
         9: ldc           #7                  // String finally  try块的finally逻辑
        11: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        14: aload_0
        15: areturn
        16: astore_0
        17: aload_0
        18: invokevirtual #9                  // Method java/lang/Exception.getMessage:()Ljava/lang/String;
        21: pop
        22: ldc           #10                 // String catch
        24: astore_1
        25: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        28: ldc           #7                  // String finally   catch块的finally逻辑
        30: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        33: aload_1
        34: areturn
        35: astore_2
        36: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        39: ldc           #7                  // String finally    
        41: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        44: aload_2
        45: athrow
      Exception table:
         from    to  target type
             0     6    16   Class java/lang/Exception
             0     6    35   any
            16    25    35   any

  

在finally中使用return(禁止)

  finally中使用return,finally逻辑执行完,执行执行return

public static String simpleTryCatch() {
        try {
            testNPE();
            return "ok";
        } catch (Exception e) {
            e.getMessage();
            return "catch";
        }finally {
            System.out.println("finally");
            return "finally";
        }
    }


字节码:

public static java.lang.String simpleTryCatch();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=0
         0: invokestatic  #5                  // Method testNPE:()V
         3: ldc           #6                  // String ok
         5: astore_0
         6: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
         9: ldc           #7                  // String finally
        11: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        14: ldc           #7                  // String finally  执行完finally逻辑,执行return
        16: areturn
        17: astore_0
        18: aload_0
        19: invokevirtual #9                  // Method java/lang/Exception.getMessage:()Ljava/lang/String;
        22: pop
        23: ldc           #10                 // String catch
        25: astore_1
        26: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        29: ldc           #7                  // String finally
        31: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        34: ldc           #7                  // String finally 执行完finally逻辑,执行return
        36: areturn
        37: astore_2
        38: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        41: ldc           #7                  // String finally
        43: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        46: ldc           #7                  // String finally 执行完finally逻辑,执行return
        48: areturn
      Exception table:
         from    to  target type
             0     6    17   Class java/lang/Exception
             0     6    37   any
            17    26    37   any