美文网首页
JVM--异常捕获及查阅字节码

JVM--异常捕获及查阅字节码

作者: _fatef | 来源:发表于2018-10-09 10:45 被阅读0次

    异常的基本概念

    • 在Java 语言规范中,所有异常都是 Throwable 类或其子类的实例。 Throwable 有两大直接子类。
      1. Error: 涵盖程序不应捕获的异常。当程序触发 Error 时, 它的执行状态已经无法恢复,需要中止线程甚至中止虚拟机。
      2. Exception:涵盖程序可能需要捕获并且处理的异常。
        Exception 有一个特殊子类 RuntimeException,用来表示“程序虽然无法继续执行,但是还能抢救一下”的情况。
    • 异常实例的构造十分昂贵。这是由于在构造异常实例时,Java虚拟机便需要生成该异常的栈轨迹(stack trace)该操作会逐一访问当前线程的 Java 栈帧,并且记录下各种调试信息,包括栈帧所指向方法的名字,方法所在类名、文件名,以及在代码中第几行触发该异常

    JVM如何捕获异常

    • 在编译生成的字节码中,每个方法都附带一个异常表。异常表中的每一个条目代表一个异常处理器,并且由 from 指针、to 指针、target 指针以及所捕获的异常类型构成。这些指针的值是字节码索引(bytecode index,bci),用以定位字节码。
    • from 指针和 to 指针标示了该异常处理器所监控的起始位置。target 指针则指向异常处理器的起始位置。
    
    public static void main(String[] args) {
      try {
        mayThrowException();
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
    // 对应的 Java 字节码
    public static void main(java.lang.String[]);
      Code:
        0: invokestatic mayThrowException:()V
        3: goto 11
        6: astore_1
        7: aload_1
        8: invokevirtual java.lang.Exception.printStackTrace
       11: return
      Exception table:
        from  to target type
          0   3   6  Class java/lang/Exception  // 异常表条目
    
    

    编译过后,该方法的异常表拥有一个条目。其 from 指针和 to 指针分别为 0 和 3,代表它的监控范围从索引为 0 的字节码开始,到索引为 3 的字节码结束(不包括 3)。该条目的 target 指针是 6, 代表这个异常处理器总索引为 6 的字节码开始。条目的最后一列,代表异常处理器所捕获的异常类型正是 Exception。

    • 当程序触发异常时, JVM 会从上至下遍历异常表中的所有条目。当触发异常的字节码的索引值在某个异常表条目的监控范围内,JVM会判断所抛出的异常和该条目想要捕获的异常是否匹配。如果匹配,JVM 会将控制流转移至该条目 target 指针指向的字节码。


    javap:查阅Java 字节码

    如下代码

    public class Foo {
      private int tryBlock;
      private int catchBlock;
      private int finallyBlock;
      private int methodExit;
    
      public void test() {
        try {
          tryBlock = 0;
        } catch (Exception e) {
          catchBlock = 1;
        } finally {
          finallyBlock = 2;
        }
        methodExit = 3;
      }
    }
    

    编译之后,使用javap 来查阅 Foo.test 方法的字节码

    $ javac Foo.java
    $ javap -p -v Foo
    Classfile ../Foo.class
      Last modified ..; size 541 bytes
      MD5 checksum 3828cdfbba56fea1da6c8d94fd13b20d
      Compiled from "Foo.java"
    public class Foo
      minor version: 0
      major version: 54
      flags: (0x0021) ACC_PUBLIC, ACC_SUPER
      this_class: #7                          // Foo
      super_class: #8                         // java/lang/Object
      interfaces: 0, fields: 4, methods: 2, attributes: 1
    Constant pool:
       #1 = Methodref          #8.#23         // java/lang/Object."<init>":()V
       #2 = Fieldref           #7.#24         // Foo.tryBlock:I
       #3 = Fieldref           #7.#25         // Foo.finallyBlock:I
       #4 = Class              #26            // java/lang/Exception
       #5 = Fieldref           #7.#27         // Foo.catchBlock:I
       #6 = Fieldref           #7.#28         // Foo.methodExit:I
       #7 = Class              #29            // Foo
       #8 = Class              #30            // java/lang/Object
       #9 = Utf8               tryBlock
      #10 = Utf8               I
      #11 = Utf8               catchBlock
      #12 = Utf8               finallyBlock
      #13 = Utf8               methodExit
      #14 = Utf8               <init>
      #15 = Utf8               ()V
      #16 = Utf8               Code
      #17 = Utf8               LineNumberTable
      #18 = Utf8               test
      #19 = Utf8               StackMapTable
      #20 = Class              #31            // java/lang/Throwable
      #21 = Utf8               SourceFile
      #22 = Utf8               Foo.java
      #23 = NameAndType        #14:#15        // "<init>":()V
      #24 = NameAndType        #9:#10         // tryBlock:I
      #25 = NameAndType        #12:#10        // finallyBlock:I
      #26 = Utf8               java/lang/Exception
      #27 = NameAndType        #11:#10        // catchBlock:I
      #28 = NameAndType        #13:#10        // methodExit:I
      #29 = Utf8               Foo
      #30 = Utf8               java/lang/Object
      #31 = Utf8               java/lang/Throwable
    {
      private int tryBlock;
        descriptor: I
        flags: (0x0002) ACC_PRIVATE
    
      private int catchBlock;
        descriptor: I
        flags: (0x0002) ACC_PRIVATE
    
      private int finallyBlock;
        descriptor: I
        flags: (0x0002) ACC_PRIVATE
    
      private int methodExit;
        descriptor: I
        flags: (0x0002) ACC_PRIVATE
    
      public Foo();
        descriptor: ()V
        flags: (0x0001) ACC_PUBLIC
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: return
          LineNumberTable:
            line 1: 0
    
      public void test();
        descriptor: ()V
        flags: (0x0001) ACC_PUBLIC
        Code:
          stack=2, locals=3, args_size=1
             0: aload_0
             1: iconst_0
             2: putfield      #2                  // Field tryBlock:I
             5: aload_0
             6: iconst_2
             7: putfield      #3                  // Field finallyBlock:I
            10: goto          35
            13: astore_1
            14: aload_0
            15: iconst_1
            16: putfield      #5                  // Field catchBlock:I
            19: aload_0
            20: iconst_2
            21: putfield      #3                  // Field finallyBlock:I
            24: goto          35
            27: astore_2
            28: aload_0
            29: iconst_2
            30: putfield      #3                  // Field finallyBlock:I
            33: aload_2
            34: athrow
            35: aload_0
            36: iconst_3
            37: putfield      #6                  // Field methodExit:I
            40: return
          Exception table:
             from    to  target type
                 0     5    13   Class java/lang/Exception
                 0     5    27   any
                13    19    27   any
          LineNumberTable:
            line 9: 0
            line 13: 5
            line 14: 10
            line 10: 13
            line 11: 14
            line 13: 19
            line 14: 24
            line 13: 27
            line 14: 33
            line 15: 35
            line 16: 40
          StackMapTable: number_of_entries = 3
            frame_type = 77 /* same_locals_1_stack_item */
              stack = [ class java/lang/Exception ]
            frame_type = 77 /* same_locals_1_stack_item */
              stack = [ class java/lang/Throwable ]
            frame_type = 7 /* same */
    }
    SourceFile: "Foo.java"
    

    注:javap 默认会打印所有非私有的字段和方法,加上 -p 选项,它还打印私有的字段和方法。 -v 选项会尽可能地打印所有信息。若只查阅对应的字节码,可用 -c来替换 -v

    1. 基本信息,涵盖了原 class 文件的相关信息
      class 文件的版本号(minor version: 0,major version: 54),该类的访问权限(flags: (0x0021) ACC_PUBLIC, ACC_SUPER),该类(this_class: #7)以及父类(super_class:#8)的名字等等。
    Classfile ../Foo.class
      Last modified ..; size 541 bytes
      MD5 checksum 3828cdfbba56fea1da6c8d94fd13b20d
      Compiled from "Foo.java"
    public class Foo
      minor version: 0   
      major version: 54
      flags: (0x0021) ACC_PUBLIC, ACC_SUPER
      this_class: #7                          // Foo
      super_class: #8                         // java/lang/Object
      interfaces: 0, fields: 4, methods: 2, attributes: 1
    
    1. 常量池,用来存放各种常量以及符号引用。
      常量池中的每一项都有一个对应的索引(如 #1),并且可能引其他的常量池项(#1 = Methodref #8.#23)。
    Constant pool:
       #1 = Methodref          #8.#23         // java/lang/Object."<init>":()V
    ... 
       #8 = Class              #30            // java/lang/Object
    ...
      #14 = Utf8               <init>
      #15 = Utf8               ()V
    ...
      #23 = NameAndType        #14:#15        // "<init>":()V
    ...
      #30 = Utf8               java/lang/Object
    
    可以将其看成一个树结构,如下:
    1. 字段区域,用来列举该类中的各个字段
      最主要的信息是该字段的类型(descriptor:I) 以及访问权限 (flags:(0x0002)ACC_PRIVATE)。对于final 的静态字段而言,如果它是基本类型或者字符串类型,那么字段区域还将包括它的常量值。
      private int tryBlock;
        descriptor: I
        flags: (0x0002) ACC_PRIVATE
    
    1. 方法区域,用来列举该类中的各个方法。
      处理方法描述符以及访问权限之外,每个方法还包括最为重要的代码区域(Code:).
      public void test();
        descriptor: ()V
        flags: (0x0001) ACC_PUBLIC
        Code:
          stack=2, locals=3, args_size=1
             0: aload_0
             1: iconst_0
             2: putfield      #2                  // Field tryBlock:I
             5: aload_0
             6: iconst_2
             7: putfield      #3                  // Field finallyBlock:I
            10: goto          35
            13: astore_1
            14: aload_0
            15: iconst_1
            16: putfield      #5                  // Field catchBlock:I
            19: aload_0
            20: iconst_2
            21: putfield      #3                  // Field finallyBlock:I
            24: goto          35
            27: astore_2
            28: aload_0
            29: iconst_2
            30: putfield      #3                  // Field finallyBlock:I
            33: aload_2
            34: athrow
            35: aload_0
            36: iconst_3
            37: putfield      #6                  // Field methodExit:I
            40: return
          Exception table:
             from    to  target type
                 0     5    13   Class java/lang/Exception
                 0     5    27   any
                13    19    27   any
          LineNumberTable:
            line 9: 0
            line 13: 5
            line 14: 10
            line 10: 13
            line 11: 14
            line 13: 19
            line 14: 24
            line 13: 27
            line 14: 33
            line 15: 35
            line 16: 40
          StackMapTable: number_of_entries = 3
            frame_type = 77 /* same_locals_1_stack_item */
              stack = [ class java/lang/Exception ]
            frame_type = 77 /* same_locals_1_stack_item */
              stack = [ class java/lang/Throwable ]
            frame_type = 7 /* same */
    

    代码区域一开始会声明方法中的操作数栈(stack=2)和局部变量数目(locals=3)的最大值,以及该方法接收参数的个数(args_size=1)。(此处的局部变量是指字节码中的局部变量而非Java 程序中的局部变量)

    相关文章

      网友评论

          本文标题:JVM--异常捕获及查阅字节码

          本文链接:https://www.haomeiwen.com/subject/psnioftx.html