• synchronized关键字所生成的字节码详细分析


    在之前已经将如下这样的源文件对应的字节码文件完整的分析完了,如下:

    这次再来写一个内容稍丰富一点的类,准备再来从头至尾的来分析一下,对其字节码的理解进一步巩固,如下:

    然后用javap -verbose来查看一下反编译信息:

    xiongweideMacBook-Pro:jvm_lectue xiongwei$ cd out/production/classes/
    xiongweideMacBook-Pro:classes xiongwei$ javap -verbose com/jvm/bytecode/MyTest2.class 
    Classfile /Users/xiongwei/Documents/workspace/IntelliJSpace/jvm_lectue/out/production/classes/com/jvm/bytecode/MyTest2.class
      Last modified Sep 11, 2018; size 831 bytes
      MD5 checksum 22c52151c374d39068e39938d1b3e72c
      Compiled from "MyTest2.java"
    public class com.jvm.bytecode.MyTest2
      minor version: 0
      major version: 52
      flags: ACC_PUBLIC, ACC_SUPER
    Constant pool:
       #1 = Methodref          #10.#34        // java/lang/Object."<init>":()V
       #2 = String             #35            // Welcome
       #3 = Fieldref           #5.#36         // com/jvm/bytecode/MyTest2.str:Ljava/lang/String;
       #4 = Fieldref           #5.#37         // com/jvm/bytecode/MyTest2.x:I
       #5 = Class              #38            // com/jvm/bytecode/MyTest2
       #6 = Methodref          #5.#34         // com/jvm/bytecode/MyTest2."<init>":()V
       #7 = Methodref          #5.#39         // com/jvm/bytecode/MyTest2.setX:(I)V
       #8 = Methodref          #40.#41        // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
       #9 = Fieldref           #5.#42         // com/jvm/bytecode/MyTest2.in:Ljava/lang/Integer;
      #10 = Class              #43            // java/lang/Object
      #11 = Utf8               str
      #12 = Utf8               Ljava/lang/String;
      #13 = Utf8               x
      #14 = Utf8               I
      #15 = Utf8               in
      #16 = Utf8               Ljava/lang/Integer;
      #17 = Utf8               <init>
      #18 = Utf8               ()V
      #19 = Utf8               Code
      #20 = Utf8               LineNumberTable
      #21 = Utf8               LocalVariableTable
      #22 = Utf8               this
      #23 = Utf8               Lcom/jvm/bytecode/MyTest2;
      #24 = Utf8               main
      #25 = Utf8               ([Ljava/lang/String;)V
      #26 = Utf8               args
      #27 = Utf8               [Ljava/lang/String;
      #28 = Utf8               myTest2
      #29 = Utf8               setX
      #30 = Utf8               (I)V
      #31 = Utf8               <clinit>
      #32 = Utf8               SourceFile
      #33 = Utf8               MyTest2.java
      #34 = NameAndType        #17:#18        // "<init>":()V
      #35 = Utf8               Welcome
      #36 = NameAndType        #11:#12        // str:Ljava/lang/String;
      #37 = NameAndType        #13:#14        // x:I
      #38 = Utf8               com/jvm/bytecode/MyTest2
      #39 = NameAndType        #29:#30        // setX:(I)V
      #40 = Class              #44            // java/lang/Integer
      #41 = NameAndType        #45:#46        // valueOf:(I)Ljava/lang/Integer;
      #42 = NameAndType        #15:#16        // in:Ljava/lang/Integer;
      #43 = Utf8               java/lang/Object
      #44 = Utf8               java/lang/Integer
      #45 = Utf8               valueOf
      #46 = Utf8               (I)Ljava/lang/Integer;
    {
      java.lang.String str;
        descriptor: Ljava/lang/String;
        flags:
    
      public static java.lang.Integer in;
        descriptor: Ljava/lang/Integer;
        flags: ACC_PUBLIC, ACC_STATIC
    
      public com.jvm.bytecode.MyTest2();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=1, args_size=1
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: aload_0
             5: ldc           #2                  // String Welcome
             7: putfield      #3                  // Field str:Ljava/lang/String;
            10: aload_0
            11: iconst_5
            12: putfield      #4                  // Field x:I
            15: return
          LineNumberTable:
            line 3: 0
            line 4: 4
            line 6: 10
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      16     0  this   Lcom/jvm/bytecode/MyTest2;
    
      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           #5                  // class com/jvm/bytecode/MyTest2
             3: dup
             4: invokespecial #6                  // Method "<init>":()V
             7: astore_1
             8: aload_1
             9: bipush        8
            11: invokevirtual #7                  // Method setX:(I)V
            14: bipush        20
            16: invokestatic  #8                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
            19: putstatic     #9                  // Field in:Ljava/lang/Integer;
            22: return
          LineNumberTable:
            line 11: 0
            line 13: 8
            line 15: 14
            line 16: 22
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      23     0  args   [Ljava/lang/String;
                8      15     1 myTest2   Lcom/jvm/bytecode/MyTest2;
    
      public void setX(int);
        descriptor: (I)V
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=2, args_size=2
             0: aload_0
             1: iload_1
             2: putfield      #4                  // Field x:I
             5: return
          LineNumberTable:
            line 19: 0
            line 20: 5
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       6     0  this   Lcom/jvm/bytecode/MyTest2;
                0       6     1     x   I
    
      static {};
        descriptor: ()V
        flags: ACC_STATIC
        Code:
          stack=1, locals=0, args_size=0
             0: bipush        10
             2: invokestatic  #8                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
             5: putstatic     #9                  // Field in:Ljava/lang/Integer;
             8: return
          LineNumberTable:
            line 8: 0
    }
    SourceFile: "MyTest2.java"

    由于这次的类中声明有静态变量,所以标红处多了一个static块,下面将“-verbose”参数去掉可以看到一个更精简的信息,如下:

    其中注意此时有一个setX()方法的,那如果将setX()方法访问修饰符由“public”改为"private",看会发生啥:

    为啥呢?其实并非私有的方法在字节码信息中就不存在了,而是用javap命令需要再加一个参数才能看到私有的方法,如下:

    由于此篇讨论的话题是关于synchronized关键字在字节码中的含义,所以接下来咱们给setX()方法加一个同步锁然后再来分析其字节码,在加之前先来看下不带synchronized所对应字节码的信息,如下:

    接下来给方法加上synchronized,如下:

    然后看一下此时对应的字节码会发生啥变化:

    貌似网上有很多资料看到对于同步方法在字节码中表现通常会有“moniterenter”和“moniterexit”,但是为啥咱们自己通过javap -verbose查看不到呢?因为修饰的是实例方法,也就是给当前对象上锁,表现则是在方法说明之上而非代码中,如果改为修饰方法中的代码的话则就会可以看到啦,如下:

    当然上面的这个是对参数str进行上锁,实际没啥意义,因为str是一个可变的值,正常的做法应该是对一个不可变的对象进行上锁,这里只是为了说明问题,下面再来看一下字节码信息:

    private void test(java.lang.String);
        descriptor: (Ljava/lang/String;)V
        flags: ACC_PRIVATE
        Code:
          stack=3, locals=4, args_size=2
             0: aload_1
             1: dup
             2: astore_2
             3: monitorenter
             4: getstatic     #10                 // Field java/lang/System.out:Ljava/io/PrintStream;
             7: ldc           #11                 // String hello world~
             9: iconst_0
            10: anewarray     #12                 // class java/lang/Object
            13: invokevirtual #13                 // Method java/io/PrintStream.printf:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
            16: pop
            17: aload_2
            18: monitorexit
            19: goto          27
            22: astore_3
            23: aload_2
            24: monitorexit
            25: aload_3
            26: athrow
            27: return
          Exception table:
             from    to  target type
                 4    19    22   any
                22    25    22   any
          LineNumberTable:
            line 23: 0
            line 24: 4
            line 25: 17
            line 26: 27
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      28     0  this   Lcom/jvm/bytecode/MyTest2;
                0      28     1   str   Ljava/lang/String;
          StackMapTable: number_of_entries = 2
            frame_type = 255 /* full_frame */
              offset_delta = 22
              locals = [ class com/jvm/bytecode/MyTest2, class java/lang/String, class java/lang/Object ]
              stack = [ class java/lang/Throwable ]
            frame_type = 250 /* chop */
              offset_delta = 4

    确实是看到了monitorenter,monitorexit了,其加了同步锁在字节码的流程可以大致分析一下:

    另外synchronized还可以修饰静态方法,下面写一个:

    很显然它锁的是整个class对象,下面也来看一下它对应的字节码信息:

    由于synchronized修饰的是方法,而非它里面的代码,所以在javap中只看到了方法签名上有同步信息。

    关于同步锁需要了解一下如下概念:
    重入锁:所谓重入锁就是说同一个线程可以访问多个同步方法,比如说:

    访问一次同步方法其引用计数就加1,像上面的引用计数就会变为2,而当setB()执行完之后,则引用计数就会减为1,再setX()执行完则引用计数会减为0。

    非重入锁:是指多个线程而言的,比如一个线程访问了一个同步方法,另一个线程再访问同一个对象的该方法则会阻塞。

  • 相关阅读:
    CNZZ友盟访问明细的采集办法
    实时采集新加坡交易所A50指数
    深入理解DIP、IoC、DI以及IoC容器
    PHP依赖注入(DI)和控制反转(IoC)详解
    PHP 的命令行模式
    浅析PHP中的闭包和匿名函数
    PHP_CodeSniffer 使用攻略
    PSR规范学习笔记
    Composer 的简介、安装及使用
    win7下使用手动安装composer
  • 原文地址:https://www.cnblogs.com/webor2006/p/9595300.html
Copyright © 2020-2023  润新知