• 07 Java源码字节码层面简单分析


    1-1 对象/实例初始化(字节码层面)

    1-1-1 静态成员初始化:cinit()V

    静态变量与静态代码块

    ()V:静态变量的初始化再字节码层面是当作class初始化,cinit是class initialize的缩写。

    public class Demo3_8_1 {
        static int i = 10;
        static {
            i = 20;
    	} static {
    		i = 30;
    	}
    }
    

    静态变量初始化字节码(代码块与单独的静态变量会收集在一起)

        static {};
        descriptor: ()V
        flags: ACC_STATIC
        Code:
          stack=1, locals=0, args_size=0
             0: bipush        10
             2: putstatic     #2                  // Field i:I
             5: bipush        20
             7: putstatic     #2                  // Field i:I
            10: bipush        30
            12: putstatic     #2                  // Field i:I
            15: return
          LineNumberTable:
            line 4: 0
            line 6: 5
            line 8: 10
            line 9: 15
    

    总结:无论是静态变量还是静态代码块,编译器会按从上至下的顺序,收集所有 static 静态代码块和静态成员赋值的代码合并为一个特殊的方法: ()V

    • 上面静态变量的最终值最后一次赋值为30

    1-1-2 实例的初始化: init()V

    test1类

    package part3;
    public class test1 {
        private String a = "s1";
        {
            b = 20;
        }
        private int b = 10;
        {
            a = "s2";
        }
        public test1(String a, int b) {
            this.a = a;
            this.b = b;
        }
    
        public static void main(String[] args) {
            test1 d = new test1("s3",30);
            System.out.println(d.a);    // s3
            System.out.println(d.b);    // 30
        }
    }
    
    

    类初始化对应的字节码(字节码层面将代码块与构造函数的字节码统一到init:V内部)

          Code:
          stack=2, locals=3, args_size=3
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: aload_0
             5: ldc           #2                  // String s1
             7: putfield      #3                  // Field a:Ljava/lang/String;
            10: aload_0
            11: bipush        20
            13: putfield      #4                  // Field b:I
            16: aload_0
            17: bipush        10
            19: putfield      #4                  // Field b:I
            22: aload_0
            23: ldc           #5                  // String s2
            25: putfield      #3                  // Field a:Ljava/lang/String;
            28: aload_0
            29: aload_1
            30: putfield      #3                  // Field a:Ljava/lang/String;
            33: aload_0
            34: iload_2
            35: putfield      #4                  // Field b:I
            38: return
            LineNumberTable: ...
            LocalVariableTable:
            Start Length Slot Name Signature
            0 39 0 this Lcn/itcast/jvm/t3/bytecode/Demo3_8_2;
            0 39 1 a Ljava/lang/String;
            0 39 2 b I
            MethodParameters: ...
    
    • 编译器会按从上至下的顺序,收集所有 {} 代码块和成员变量赋值的代码,形成新的构造方法,但原始构造方法内的代码总是在最后

    1-2 方法初始化(动态绑定与静态绑定在字节码层面的体现)

    源码

    package part3;
    public class demo {
        public demo(){}
        private void test1(){}
        private final void test2(){}
        public void test3(){}
        public static void test4(){}
        protected void test5(){};
        void test6(){};
        public static void main(String[] args) {
            demo d = new demo();
            d.test1();
            d.test2();
            d.test3();
            d.test4();
            demo.test4();
            d.test5();
            d.test6();
        }
    }
    
    

    main函数字节码

    
      public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=2, args_size=1
             0: new           #2                  // class part3/demo    创建未初始化的对象引用
             3: dup                               // 复制栈顶对象引用,并入栈
             4: invokespecial #3                  // Method "<init>":()V ,调用对象初始化方法,初始化实例,
             7: astore_1                          // 存储实例引用
             8: aload_1                           // 加载实例引用
             9: invokespecial #4                  // Method test1:()V
            12: aload_1
            13: invokespecial #5                  // Method test2:()V
            16: aload_1
            17: invokevirtual #6                  // Method test3:()V
            20: aload_1
            21: pop                               // 静态方法不需要用到实例对象,所以将实例对象从操作数栈弹出
            22: invokestatic  #7                  // Method test4:()V
            25: invokestatic  #7                  // Method test4:()V
            28: aload_1
            29: invokevirtual #8                  // Method test5:()V
            32: aload_1
            33: invokevirtual #9                  // Method test6:()V
            36: return
          LineNumberTable:
            line 12: 0
            line 13: 8
            line 14: 12
            line 15: 16
            line 16: 20
            line 17: 25
            line 18: 28
            line 19: 32
            line 20: 36
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      37     0  args   [Ljava/lang/String;
                8      29     1     d   Lpart3/demo;
    }
    SourceFile: "demo.java"
    

    1-2-1方法调用的字节码指令总结(重要)

    命令 适用的函数 特点
    invokespecial private和构造函数,以及final修饰函数 静态绑定函数,转换为字节码的时候就已经确定
    invokestatic static修饰的函数 静态绑定函数,转换为字节码的时候就已经确定
    invokevirtual,invokeinterface(通过接口引用调用方法) public和protected修饰的函数,默认的函数 动态绑定函数,链接的时候根据vtable查找执行方法的字节码

    1-3 多态的原理

    工具的具体使用可以参考:Java字节码角度分析多态原理 ——提升硬实力8

    1-3-1 简单的多态实例:通过父类引用子类对象。

    package part3;
    import java.io.IOException;
    /**
     * 演示多态原理,注意加上下面的 JVM 参数,禁用指针压缩,指针压缩会将类的实际地址进行压缩。
     * -XX:-UseCompressedOops -XX:-UseCompressedClassPointers
     */
    public class test2 {
        public static void test(Animal animal) {
            animal.eat();
            System.out.println(animal.toString());
        }
        public static void main(String[] args) throws IOException {
            test(new Cat());
            test(new Dog());
            System.in.read();
        }
    }
    
    abstract class Animal {
        public abstract void eat();
        @Override
        public String toString() {
            return "我是" + this.getClass().getSimpleName();
        }
    }
    
    class Dog extends Animal {
    
        @Override
        public void eat() {
            System.out.println("啃骨头");
        }
    }
    
    class Cat extends Animal {
    
        @Override
        public void eat() {
            System.out.println("吃鱼");
        }
    }
    
    

    执行结果

    吃鱼
    我是Cat
    啃骨头
    我是Dog
    

    原理:Java多态的实现是通过方法区的类的方法表(vtable)查表确定具体调用哪一个方法,方法的确定是在类加载的过程中链接时确定的,因此称为动态绑定。

    1-3-2 多态原理总结(重要)

    注意:动态绑定的函数才叫多态

    构造函数为什么不能是虚函数

    当执行 invokevirtual 指令时,
    1. 先通过栈帧中的对象引用找到对象
    2. 分析对象头,找到对象的实际 Class(注意对象头中包含mark word和Klass word,Kclass word是一个指针,指向对象所从属的class)
    3. Class 结构中有 vtable,它在类加载的链接阶段就已经根据方法的重写规则生成好了
    4. 查表得到方法的具体地址
    5. 执行方法的字节码
    

    Java的多态原理

    1-3-3 学会借助HSDB 工具查看类的结构

    操作步骤

    step1: 运行查看程序,通过jps获取程序的ID,然后运行HDSB工具。
    在JDK包安装目录下使用该命令:java -cp ./lib/sa-jdi.jar sun.jvm.hotspot.HSDB
    
    C:Program FilesJavajdk1.8.0_131>java -cp ./lib/sa-jdi.jar sun.jvm.hotspot.HSDB
    

    step2:根据进程PID,让工具连接进程(attach to hotspot process)。
    

    step3:通过tools->class browser找到对应的Dog class
    

    1-4 字节码层面的异常的处理体现

    1-4-1 try-catch 字节码命令实例

    源码+code部分字节码

       public class test3 {
        public static void main(String[] args) {
            int i = 0;
            try {
                i = 10;
            } catch (Exception e) {
                i = 20;
            }
        }
    }
    ========================================================================================================================
      Code:
          stack=1, locals=3, args_size=1
             0: iconst_0
             1: istore_1
             2: bipush        10           
             4: istore_1              // 2,4行作用即 i = 10
             5: goto          12
             8: astore_2            //  astore_2 是将异常对象引用存入局部变量表的 slot 2 位置
             9: bipush        20
            11: istore_1           //   9,11作用 即 i = 20
            12: return
          Exception table:           // 该处代码的异常表,检测范围是从from到to即[2,5),在这个范围内的代码出现异常都会被检测          
             from    to  target type
                 2     5     8   Class java/lang/Exception
          LineNumberTable:
            line 5: 0
            line 7: 2
            line 10: 5
            line 8: 8
            line 9: 9
            line 11: 12
          LocalVariableTable:                             // 局部变量表中slot2用于存储异常对象的引用
            Start  Length  Slot  Name   Signature
                9       3     2     e   Ljava/lang/Exception;
                0      13     0  args   [Ljava/lang/String;
                2      11     1     i   I
          StackMapTable: number_of_entries = 2
            frame_type = 255 /* full_frame */
              offset_delta = 8
              locals = [ class "[Ljava/lang/String;", int ]
              stack = [ class java/lang/Exception ]
            frame_type = 3 /* same */
    }
    
    总结:
    • 字节码指令通过Exception table 的结构检测固定范围内的字节码执行异常
      • 如果出现异常需要匹配异常的类型,并使得指令跳转到执行位置。
      • 没有异常,在上面的示例中catch代码块由于go to 命令不会执行。

    1-4-2 单个try多个catch的异常处理

    源码+code部分字节码

    public class test4 {
        public static void main(String[] args) {
            int i = 0;
            try {
                i = 10;
            } catch (ArithmeticException e) {
                i = 30;
            } catch (NullPointerException e) {
                i = 40;
            } catch (Exception e) {
                i = 50;
            }
        }
    }
    ======================================================================================================
        Code:
          stack=1, locals=3, args_size=1
             0: iconst_0
             1: istore_1
             2: bipush        10
             4: istore_1
             5: goto          26
             8: astore_2
             9: bipush        30
            11: istore_1
            12: goto          26
            15: astore_2
            16: bipush        40
            18: istore_1
            19: goto          26
            22: astore_2
            23: bipush        50
            25: istore_1
            26: return
          Exception table:
             from    to  target type
                 2     5     8   Class java/lang/ArithmeticException
                 2     5    15   Class java/lang/NullPointerException
                 2     5    22   Class java/lang/Exception
          LineNumberTable:
            line 5: 0
            line 7: 2
            line 14: 5
            line 8: 8
            line 9: 9
            line 14: 12
            line 10: 15
            line 11: 16
            line 14: 19
            line 12: 22
            line 13: 23
            line 15: 26
          LocalVariableTable:                            // 三种类型的异常无论哪种发生都只会存储在局部变量表的slot2位置
            Start  Length  Slot  Name   Signature
                9       3     2     e   Ljava/lang/ArithmeticException;
               16       3     2     e   Ljava/lang/NullPointerException;
               23       3     2     e   Ljava/lang/Exception;
                0      27     0  args   [Ljava/lang/String;
                2      25     1     i   I
          StackMapTable: number_of_entries = 4
            frame_type = 255 /* full_frame */
              offset_delta = 8
              locals = [ class "[Ljava/lang/String;", int ]
              stack = [ class java/lang/ArithmeticException ]
            frame_type = 70 /* same_locals_1_stack_item */
              stack = [ class java/lang/NullPointerException ]
            frame_type = 70 /* same_locals_1_stack_item */
              stack = [ class java/lang/Exception ]
            frame_type = 3 /* same */
    }
    SourceFile: "test4.java"
    

    总结:单个try多个catch情况下,异常出现时,只能进入 Exception table 中一个分支,所以局部变量表 slot 2 位置被共用

    1-4-3 multi-catch 的情况(JDK1.7新支持的语法)

    源码+code部分字节码

    package part3;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    public class test5 {
            public static void main(String[] args){
                try{
                    Method test = test5.class.getMethod("test");
                    test.invoke("test");
                }catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e){
                    e.printStackTrace();
                }
            }
            public static void test() {
                System.out.println("ok");
            }
    }
    ===========================================================================================================s
      public part3.test5();
        descriptor: ()V
        flags: 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 6: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       5     0  this   Lpart3/test5;
    
      public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=3, locals=2, args_size=1
             0: ldc           #2                  // class part3/test5
             2: ldc           #3                  // String test
             4: iconst_0
             5: anewarray     #4                  // class java/lang/Class
             8: invokevirtual #5                  // Method java/lang/Class.getMethod:(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
            11: astore_1
            12: aload_1
            13: ldc           #3                  // String test
            15: iconst_0
            16: anewarray     #6                  // class java/lang/Object
            19: invokevirtual #7                  // Method java/lang/reflect/Method.invoke:(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;
            22: pop
            23: goto          31
            26: astore_1                           // 将异常引用存入到局部变量表的slot1
            27: aload_1                            // 将异常引用放入操作数栈
            28: invokevirtual #11                 // Method java/lang/ReflectiveOperationException.printStackTrace:()V
            31: return
          Exception table:                       // 异常表,发生异常跳转到同一target位置(标号为26的位置)
             from    to  target type
                 0    23    26   Class java/lang/NoSuchMethodException
                 0    23    26   Class java/lang/IllegalAccessException
                 0    23    26   Class java/lang/reflect/InvocationTargetException
          LineNumberTable:
            line 9: 0
            line 10: 12
            line 13: 23
            line 11: 26
            line 12: 27
            line 14: 31
          LocalVariableTable:                  // 局部变量表,共有2个slot,可以看到slot被方法的引用以及异常对象的引用共享
            Start  Length  Slot  Name   Signature
               12      11     1  test   Ljava/lang/reflect/Method;
               27       4     1     e   Ljava/lang/ReflectiveOperationException;
                0      32     0  args   [Ljava/lang/String;
          StackMapTable: number_of_entries = 2
            frame_type = 90 /* same_locals_1_stack_item */
              stack = [ class java/lang/ReflectiveOperationException ]
            frame_type = 4 /* same */
    
      public static void test();
        descriptor: ()V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=0, args_size=0
             0: getstatic     #12                 // Field java/lang/System.out:Ljava/io/PrintStream;
             3: ldc           #13                 // String ok
             5: invokevirtual #14                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
             8: return
          LineNumberTable:
            line 16: 0
            line 17: 8
    }
    SourceFile: "test5.java"
        
    

    总结:新增的语法,虽然有多个异常,但最多只有一个异常发生,通过将异常表中的target指向同一个目标实现异常的捕获

          Exception table:                       // 异常表,发生异常跳转到同一target位置(标号为26的位置)
             from    to  target type
                 0    23    26   Class java/lang/NoSuchMethodException
                 0    23    26   Class java/lang/IllegalAccessException
                 0    23    26   Class java/lang/reflect/InvocationTargetException
    

    1-4-4 finally关键字在字节码层面的体现

    public class test6 {
        public static void main(String[] args) {
            int i = 0;
            try {
                i = 10;
            } catch (Exception e) {
                i = 20;
            } finally {
                i = 30;
            }
        }
    }
    ============================================================================================
      public part3.test6();
        descriptor: ()V
        flags: 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 2: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       5     0  this   Lpart3/test6;
      public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=1, locals=4, args_size=1
             0: iconst_0
             1: istore_1
                      
             2: bipush        10
             4: istore_1                  // 情况1:没有异常发生后,执行finally内代码。
             5: bipush        30          // finally块内字节码第一个次出现
             7: istore_1
             8: goto          27          
                      
            11: astore_2
            12: bipush        20
            14: istore_1                  // 情况2:catch匹配到的异常后,执行finally内代码。
            15: bipush        30          // finally块内字节码第二次出现
            17: istore_1
            18: goto          27
                      
            21: astore_3                  // 情况3:catch无法匹配的异常的处理后,执行finally内代码
            22: bipush        30          // finally块内字节码第三次出现
            24: istore_1
                      
            25: aload_3                   // 无法匹配的异常会存入到局部变量表的slot3后,然后抛出。
            26: athrow                    // athrow命令会抛出异常
                      
            27: return
          Exception table:
             from    to  target type
                 2     5    11   Class java/lang/Exception
                 2     5    21   any
                11    15    21   any
          LineNumberTable:
            line 4: 0
            line 6: 2
            line 10: 5
            line 11: 8
            line 7: 11
            line 8: 12
            line 10: 15
            line 11: 18
            line 10: 21
            line 12: 27
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
               12       3     2     e   Ljava/lang/Exception;
                0      28     0  args   [Ljava/lang/String;
                2      26     1     i   I
          StackMapTable: number_of_entries = 3
            frame_type = 255 /* full_frame */
              offset_delta = 11
              locals = [ class "[Ljava/lang/String;", int ]
              stack = [ class java/lang/Exception ]
            frame_type = 73 /* same_locals_1_stack_item */
              stack = [ class java/lang/Throwable ]
            frame_type = 5 /* same */
    }
    SourceFile: "test6.java"
    

    总结:finally块内代码对应的字节码被重复了三次,分别放在了三个位置,从而确保finally块内代码一定会执行

    1-4-5 练习题:finally代码块的辨析(不要再finally里面return,会吞掉异常)

    问题:下面代码的返回结果是10还是20?

    源码+code部分字节码

    public class test7 {
        public static void main(String[] args) {
                int result = test();
                System.out.println(result);    // 20
        }
        public static int test() {
            try {
                return 10;
            } finally {
                return 20;
            }
        }
    }
    ============================================================================================ 
      public static int test();
        descriptor: ()I
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=1, locals=2, args_size=0
             0: bipush        10          // 将10放入栈顶
             2: istore_0                  // 弹出10放入slot 0
                 
             3: bipush        20          // 将20放入栈顶     (finally代码块)
             5: ireturn                   // 返回栈顶20
                 
             6: astore_1                  // 如发生异常,把异常的引用存入到slot 1     
             7: bipush        20          // 将20放入栈顶
             9: ireturn                   // 返回栈顶元素     (finally代码块)
          Exception table:
             from    to  target type
                 0     3     6   any
          LineNumberTable:
            line 10: 0
            line 12: 3
          StackMapTable: number_of_entries = 1
            frame_type = 70 /* same_locals_1_stack_item */
              stack = [ class java/lang/Throwable ]
    }
    SourceFile: "test7.java"
    

    总结:可以看到 return 10 这个语句在字节码层面的表现如下所示,并没有返回,因为必须执行finally语句之后才能返回

    0: bipush        10          // 将10放入栈顶
    2: istore_0                  // 弹出10放入slot 0
    
    • 操作数栈顶元素的返回必须执行完finally语句的字节码指令。通过将 finally 中的 ireturn 被插入了所有可能的流程,返回结果肯定以 finally 的为准
    • 1-4-4代码中finally的字节码会有athrow字节码抛出异常而这个例子里没有,这启示我们如果在 finally 中出现了 return,会吞掉异常这个非常危险

    实例finally里面return会导致程序的异常无法被捕获

    public class test7 {
        public static void main(String[] args) {
            int result = test();
            System.out.println(result);
        }
        public static int test() {
            try {
                int i = 1/0;
                return 10;
            } finally {
                return 20;
            }
        }
    }
    

    1-4-6 练习题:finally代码块的辨析

    问题:下面代码的返回结果是10还是20?

    package part3;
    public class test8 {
        public static void main(String[] args) {
            int result = test();
            System.out.println(result);      // 10
        }
        public static int test(){
            int i = 10;
            try {
                return i;
            } finally {
                i = 20;
            }
        }
    }
    =================================================================================================
    public static int test();
        descriptor: ()I
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=1, locals=3, args_size=0
             0: bipush        10
             2: istore_0                // 局部变量表的slot0:10
             3: iload_0
             4: istore_1                // 弹出,存储到局部变量表的slot1:10
                 
             5: bipush        20        // finally 语句第二次出现
             7: istore_0                //弹出,存储到局部变量表的slot0:20
             8: iload_1
             9: ireturn                 // 没有异常发生,直接将局部变量表的slot 1的值即10返回
                 
            10: astore_2                // 局部变量表的slot2:发生的异常对象引用
            11: bipush        20        // finally 语句第一次出现,此时的修改无法影响返回值
            13: istore_0                // 弹出,存储到局部变量表的slot0:20
            14: aload_2                 // 有异常发生,加载异常对象引用
                
            15: athrow                  // 抛出异常
          Exception table:
             from    to  target type
                 3     5    10   any
          LineNumberTable:
            line 10: 0
            line 12: 3
            line 14: 5
            line 12: 8
            line 14: 10
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                3      13     0     i   I
          StackMapTable: number_of_entries = 1
            frame_type = 255 /* full_frame */
              offset_delta = 10
              locals = [ int ]
              stack = [ class java/lang/Throwable ]
    }
    SourceFile: "test8.java"
    

    总结:在finally的对i的修改与返回的i的值在局部变量表的不同slot

    1-5 synchronized关键字在字节码层面的体现

    实例源码与code属性字节码

    public class test9 {
        public static void main(String[] args) {
            Object lock = new Object();
            synchronized (lock) {             /*问题:字节码层面如何保证锁的进入与退出成对?*/
                System.out.println("ok");
            }
        }
    }
    ===========================================================================
    {
      public part3.test9();
        descriptor: ()V
        flags: 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 3: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       5     0  this   Lpart3/test9;
    
      public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=4, args_size=1
             0: new           #2                  // class java/lang/Object   创建object对象,并将对象引用压入操作数栈
             3: dup                               // 复制对象引用压入操作数栈
     /*2个对象引用作用(对应4,7,8)
        1)一个弹出用于invokespecial,用于调用对象的构造方法
        2)另外一个弹出放入到局部变量表的slot1即lock变量存储对象的引用
     */
             4: invokespecial #1                  // Method java/lang/Object."<init>":()V 
             7: astore_1
             8: aload_1
    
                      
             9: dup
     /* 2个对象引用作用(对应4,7,8):
         1)一个弹出用于monitorenter,加锁
         2)另外一个弹出存储到局部变量表之后用于monitorexit,解锁。
     */         
            10: astore_2                         // 弹出,局部变量表slot2:锁对象引用
                      
            11: monitorenter                      // 加锁
                      
            12: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
            15: ldc           #4                  // String ok
            17: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            20: aload_2
                      
            21: monitorexit                       // 解锁
            22: goto          30                  // 没有发生异常则返回
      /* 
            synchronized代码块内异常处理。
      */                     
            25: astore_3                         // 存储异常引用到局部变量表slot3
            26: aload_2                          // 加载 局部变量表slot2:锁对象引用 到操作数栈
            27: monitorexit                      // 异常情况下保证解锁!!!!!!!!!!!                
            28: aload_3                          // 加载异常引用到操作数栈
            29: athrow                           // 抛出异常
            30: return
          Exception table:
             from    to  target type
                12    22    25   any
                25    28    25   any            // 25-28如果一直发生异常会一直执行,确保解锁。
          LineNumberTable:
            line 5: 0
            line 6: 8
            line 7: 12
            line 8: 20
            line 9: 30
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      31     0  args   [Ljava/lang/String;
                8      23     1  lock   Ljava/lang/Object;
          StackMapTable: number_of_entries = 2
            frame_type = 255 /* full_frame */
              offset_delta = 25
              locals = [ class "[Ljava/lang/String;", class java/lang/Object, class java/lang/Object ]
              stack = [ class java/lang/Throwable ]
            frame_type = 250 /* chop */
              offset_delta = 4
    }
    SourceFile: "test9.java"
    

    总结:锁的进入与退出需要考虑发生异常与不发生异常两种情况

    参考资料

    01 JVM基础课程

    02 JDK8:Chapter 6. The Java Virtual Machine Instruction Set

    03 JDK8:Chapter 3. Compiling for the Java Virtual Machine

    03 同类整理好的博客

    04 Java字节码角度分析多态原理 ——提升硬实力8

    05 同类整理好的博客

  • 相关阅读:
    Git工作中用法(Gitlab)
    面试记录,不定时更新中。。。
    webservice 技术改进
    HTTP详解
    Git学习05 --分支管理02
    Git学习04 --分支管理
    链表判环问题
    k-选取问题
    图片与字符串(base64编码)的转化
    二叉堆与堆排序的简单实现
  • 原文地址:https://www.cnblogs.com/kfcuj/p/14659773.html
Copyright © 2020-2023  润新知