bilibili-JVM学习笔记13 synchronized
The Java Virtual Machine Specification - Java SE 8 Edition
JVM学习笔记11 - Java字节码初识
JVM学习笔记12 - 解读笔记11中的attributes
实例方法加 synchronized
package new_package.jvm.p46;
public class SynchronizedTest1 {
private int i = 0;
public synchronized void test1() {
i++;
System.out.println(i);
}
}
javap -verbose -p new_package.jvm.p46.SynchronizedTest1
Classfile /Users/kevin/Documents/opensource/gitee/java-read-sources-sample/target/classes/new_package/jvm/p46/SynchronizedTest1.class
Last modified 2020-6-10; size 558 bytes
MD5 checksum 1a105be1126b77561dc33fa15d28ea0e
Compiled from "SynchronizedTest1.java"
public class new_package.jvm.p46.SynchronizedTest1
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#19 // java/lang/Object."<init>":()V
#2 = Fieldref #5.#20 // new_package/jvm/p46/SynchronizedTest1.i:I
#3 = Fieldref #21.#22 // java/lang/System.out:Ljava/io/PrintStream;
#4 = Methodref #23.#24 // java/io/PrintStream.println:(I)V
#5 = Class #25 // new_package/jvm/p46/SynchronizedTest1
#6 = Class #26 // java/lang/Object
#7 = Utf8 i
#8 = Utf8 I
#9 = Utf8 <init>
#10 = Utf8 ()V
#11 = Utf8 Code
#12 = Utf8 LineNumberTable
#13 = Utf8 LocalVariableTable
#14 = Utf8 this
#15 = Utf8 Lnew_package/jvm/p46/SynchronizedTest1;
#16 = Utf8 test1
#17 = Utf8 SourceFile
#18 = Utf8 SynchronizedTest1.java
#19 = NameAndType #9:#10 // "<init>":()V
#20 = NameAndType #7:#8 // i:I
#21 = Class #27 // java/lang/System
#22 = NameAndType #28:#29 // out:Ljava/io/PrintStream;
#23 = Class #30 // java/io/PrintStream
#24 = NameAndType #31:#32 // println:(I)V
#25 = Utf8 new_package/jvm/p46/SynchronizedTest1
#26 = Utf8 java/lang/Object
#27 = Utf8 java/lang/System
#28 = Utf8 out
#29 = Utf8 Ljava/io/PrintStream;
#30 = Utf8 java/io/PrintStream
#31 = Utf8 println
#32 = Utf8 (I)V
{
public new_package.jvm.p46.SynchronizedTest1();
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: iconst_0
6: putfield #2 // Field i:I
9: return
LineNumberTable:
line 8: 0
line 10: 4
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this Lnew_package/jvm/p46/SynchronizedTest1;
public synchronized void test1();
descriptor: ()V
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=3, locals=1, args_size=1
0: aload_0
1: dup
2: getfield #2 // Field i:I
5: iconst_1
6: iadd
7: putfield #2 // Field i:I
10: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
13: aload_0
14: getfield #2 // Field i:I
17: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
20: return
LineNumberTable:
line 13: 0
line 14: 10
line 15: 20
LocalVariableTable:
Start Length Slot Name Signature
0 21 0 this Lnew_package/jvm/p46/SynchronizedTest1;
}
SourceFile: "SynchronizedTest1.java"
解析:
- 并没有 monitor 指令
- 方法标识符上标有 ACC_SYNCHRONIZED
- test1 方法没有入参,然而 args_size=1
- 这是因为每个非静态方法,编译器会自动在第一个位置添加一个
参数 this
,指向当前实例
- 这是因为每个非静态方法,编译器会自动在第一个位置添加一个
实例方法体内部加 synchronized
package new_package.jvm.p46;
public class SynchronizedTest2 {
private Object lock = new Object();
private int i = 0;
public void test1() {
synchronized (lock) {
i++;
}
System.out.println(i);
}
}
javap -verbose -p new_package.jvm.p46.SynchronizedTest2
Classfile /Users/kevin/Documents/opensource/gitee/java-read-sources-sample/target/classes/new_package/jvm/p46/SynchronizedTest2.class
Last modified 2020-6-10; size 734 bytes
MD5 checksum 3ed0ca070aa2fdfc291e433ce042a21b
Compiled from "SynchronizedTest2.java"
public class new_package.jvm.p46.SynchronizedTest2
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #2.#26 // java/lang/Object."<init>":()V
#2 = Class #27 // java/lang/Object
#3 = Fieldref #7.#28 // new_package/jvm/p46/SynchronizedTest2.lock:Ljava/lang/Object;
#4 = Fieldref #7.#29 // new_package/jvm/p46/SynchronizedTest2.i:I
#5 = Fieldref #30.#31 // java/lang/System.out:Ljava/io/PrintStream;
#6 = Methodref #32.#33 // java/io/PrintStream.println:(I)V
#7 = Class #34 // new_package/jvm/p46/SynchronizedTest2
#8 = Utf8 lock
#9 = Utf8 Ljava/lang/Object;
#10 = Utf8 i
#11 = Utf8 I
#12 = Utf8 <init>
#13 = Utf8 ()V
#14 = Utf8 Code
#15 = Utf8 LineNumberTable
#16 = Utf8 LocalVariableTable
#17 = Utf8 this
#18 = Utf8 Lnew_package/jvm/p46/SynchronizedTest2;
#19 = Utf8 test1
#20 = Utf8 StackMapTable
#21 = Class #34 // new_package/jvm/p46/SynchronizedTest2
#22 = Class #27 // java/lang/Object
#23 = Class #35 // java/lang/Throwable
#24 = Utf8 SourceFile
#25 = Utf8 SynchronizedTest2.java
#26 = NameAndType #12:#13 // "<init>":()V
#27 = Utf8 java/lang/Object
#28 = NameAndType #8:#9 // lock:Ljava/lang/Object;
#29 = NameAndType #10:#11 // i:I
#30 = Class #36 // java/lang/System
#31 = NameAndType #37:#38 // out:Ljava/io/PrintStream;
#32 = Class #39 // java/io/PrintStream
#33 = NameAndType #40:#41 // println:(I)V
#34 = Utf8 new_package/jvm/p46/SynchronizedTest2
#35 = Utf8 java/lang/Throwable
#36 = Utf8 java/lang/System
#37 = Utf8 out
#38 = Utf8 Ljava/io/PrintStream;
#39 = Utf8 java/io/PrintStream
#40 = Utf8 println
#41 = Utf8 (I)V
{
public new_package.jvm.p46.SynchronizedTest2();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: new #2 // class java/lang/Object
8: dup
9: invokespecial #1 // Method java/lang/Object."<init>":()V
12: putfield #3 // Field lock:Ljava/lang/Object;
15: aload_0
16: iconst_0
17: putfield #4 // Field i:I
20: return
LineNumberTable:
line 8: 0
line 10: 4
line 12: 15
LocalVariableTable:
Start Length Slot Name Signature
0 21 0 this Lnew_package/jvm/p46/SynchronizedTest2;
public void test1();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=3, locals=3, args_size=1
0: aload_0
1: getfield #3 // Field lock:Ljava/lang/Object;
4: dup
5: astore_1
6: monitorenter
7: aload_0
8: dup
9: getfield #4 // Field i:I
12: iconst_1
13: iadd
14: putfield #4 // Field i:I
17: aload_1
18: monitorexit
19: goto 27
22: astore_2
23: aload_1
24: monitorexit
25: aload_2
26: athrow
27: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
30: aload_0
31: getfield #4 // Field i:I
34: invokevirtual #6 // Method java/io/PrintStream.println:(I)V
37: return
Exception table:
from to target type
7 19 22 any
22 25 22 any
LineNumberTable:
line 16: 0
line 17: 7
line 18: 17
line 20: 27
line 21: 37
LocalVariableTable:
Start Length Slot Name Signature
0 38 0 this Lnew_package/jvm/p46/SynchronizedTest2;
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 22
locals = [ class new_package/jvm/p46/SynchronizedTest2, class java/lang/Object ]
stack = [ class java/lang/Throwable ]
frame_type = 250 /* chop */
offset_delta = 4
}
SourceFile: "SynchronizedTest2.java"
解析:
- 存在
monitor 指令
- 有两个 monitorexit 指令,表示不同情况下的释放锁
类成员变量
package new_package.jvm.p46;
public class Test3 {
public static Integer num = 333;
}
javap -verbose -p new_package.jvm.p46.Test3
Classfile /Users/kevin/Documents/opensource/gitee/java-read-sources-sample/target/classes/new_package/jvm/p46/Test3.class
Last modified 2020-6-10; size 453 bytes
MD5 checksum 556c2f1dbbd64d51ccf97d2c59920873
Compiled from "Test3.java"
public class new_package.jvm.p46.Test3
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #5.#18 // java/lang/Object."<init>":()V
#2 = Methodref #19.#20 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
#3 = Fieldref #4.#21 // new_package/jvm/p46/Test3.num:Ljava/lang/Integer;
#4 = Class #22 // new_package/jvm/p46/Test3
#5 = Class #23 // java/lang/Object
#6 = Utf8 num
#7 = Utf8 Ljava/lang/Integer;
#8 = Utf8 <init>
#9 = Utf8 ()V
#10 = Utf8 Code
#11 = Utf8 LineNumberTable
#12 = Utf8 LocalVariableTable
#13 = Utf8 this
#14 = Utf8 Lnew_package/jvm/p46/Test3;
#15 = Utf8 <clinit>
#16 = Utf8 SourceFile
#17 = Utf8 Test3.java
#18 = NameAndType #8:#9 // "<init>":()V
#19 = Class #24 // java/lang/Integer
#20 = NameAndType #25:#26 // valueOf:(I)Ljava/lang/Integer;
#21 = NameAndType #6:#7 // num:Ljava/lang/Integer;
#22 = Utf8 new_package/jvm/p46/Test3
#23 = Utf8 java/lang/Object
#24 = Utf8 java/lang/Integer
#25 = Utf8 valueOf
#26 = Utf8 (I)Ljava/lang/Integer;
{
public static java.lang.Integer num;
descriptor: Ljava/lang/Integer;
flags: ACC_PUBLIC, ACC_STATIC
public new_package.jvm.p46.Test3();
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 8: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lnew_package/jvm/p46/Test3;
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: sipush 333
3: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
6: putstatic #3 // Field num:Ljava/lang/Integer;
9: return
LineNumberTable:
line 10: 0
}
SourceFile: "Test3.java"
解析:
- 存在自动装箱操作(常量池 #2)
<clinit>
有静态成员变量时会存在,给静态变量赋初始值- 静态变量在
<clinit>
中赋值,成员变量在 <init> 中赋值
; 静态代码块
的内容也是在<clinit>
中执行;- 不管有多少静态代码块,都会合并到一个
<clinit>
中执行;
- 不管有多少静态代码块,都会合并到一个
当静态变量不赋初始值时,不存在 <clinit>
:
public class Test3 {
public static Integer num = null;
}
Classfile /Users/kevin/Documents/opensource/gitee/java-read-sources-sample/target/classes/new_package/jvm/p46/Test3.class
Last modified 2020-6-10; size 380 bytes
MD5 checksum 421889337f1b6eb887344e7018dcf555
Compiled from "Test3.java"
public class new_package.jvm.p46.Test3
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #4.#17 // java/lang/Object."<init>":()V
#2 = Fieldref #3.#18 // new_package/jvm/p46/Test3.num:Ljava/lang/Integer;
#3 = Class #19 // new_package/jvm/p46/Test3
#4 = Class #20 // java/lang/Object
#5 = Utf8 num
#6 = Utf8 Ljava/lang/Integer;
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lnew_package/jvm/p46/Test3;
#14 = Utf8 <clinit>
#15 = Utf8 SourceFile
#16 = Utf8 Test3.java
#17 = NameAndType #7:#8 // "<init>":()V
#18 = NameAndType #5:#6 // num:Ljava/lang/Integer;
#19 = Utf8 new_package/jvm/p46/Test3
#20 = Utf8 java/lang/Object
{
public static java.lang.Integer num;
descriptor: Ljava/lang/Integer;
flags: ACC_PUBLIC, ACC_STATIC
public new_package.jvm.p46.Test3();
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 8: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lnew_package/jvm/p46/Test3;
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: aconst_null
1: putstatic #2 // Field num:Ljava/lang/Integer;
4: return
LineNumberTable:
line 10: 0
}
SourceFile: "Test3.java"
成员变量在 <init>
中赋值
package new_package.jvm.p46;
public class Test4 {
private int x = 10;
private String name = "cat";
public Test4() {
}
public Test4(int x) {
this.x = x;
}
}
javap -verbose -p new_package.jvm.p46.Test4
Classfile /Users/kevin/Documents/opensource/gitee/java-read-sources-sample/target/classes/new_package/jvm/p46/Test4.class
Last modified 2020-6-10; size 496 bytes
MD5 checksum b13c2fd8ac5f933ab8ccb230baa6940b
Compiled from "Test4.java"
public class new_package.jvm.p46.Test4
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#21 // java/lang/Object."<init>":()V
#2 = Fieldref #5.#22 // new_package/jvm/p46/Test4.x:I
#3 = String #23 // cat
#4 = Fieldref #5.#24 // new_package/jvm/p46/Test4.name:Ljava/lang/String;
#5 = Class #25 // new_package/jvm/p46/Test4
#6 = Class #26 // java/lang/Object
#7 = Utf8 x
#8 = Utf8 I
#9 = Utf8 name
#10 = Utf8 Ljava/lang/String;
#11 = Utf8 <init>
#12 = Utf8 ()V
#13 = Utf8 Code
#14 = Utf8 LineNumberTable
#15 = Utf8 LocalVariableTable
#16 = Utf8 this
#17 = Utf8 Lnew_package/jvm/p46/Test4;
#18 = Utf8 (I)V
#19 = Utf8 SourceFile
#20 = Utf8 Test4.java
#21 = NameAndType #11:#12 // "<init>":()V
#22 = NameAndType #7:#8 // x:I
#23 = Utf8 cat
#24 = NameAndType #9:#10 // name:Ljava/lang/String;
#25 = Utf8 new_package/jvm/p46/Test4
#26 = Utf8 java/lang/Object
{
private int x;
descriptor: I
flags: ACC_PRIVATE
private java.lang.String name;
descriptor: Ljava/lang/String;
flags: ACC_PRIVATE
public new_package.jvm.p46.Test4();
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: bipush 10
7: putfield #2 // Field x:I
10: aload_0
11: ldc #3 // String cat
13: putfield #4 // Field name:Ljava/lang/String;
16: return
LineNumberTable:
line 14: 0
line 10: 4
line 12: 10
line 15: 16
LocalVariableTable:
Start Length Slot Name Signature
0 17 0 this Lnew_package/jvm/p46/Test4;
public new_package.jvm.p46.Test4(int);
descriptor: (I)V
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: bipush 10
7: putfield #2 // Field x:I
10: aload_0
11: ldc #3 // String cat
13: putfield #4 // Field name:Ljava/lang/String;
16: aload_0
17: iload_1
18: putfield #2 // Field x:I
21: return
LineNumberTable:
line 17: 0
line 10: 4
line 12: 10
line 18: 16
line 19: 21
LocalVariableTable:
Start Length Slot Name Signature
0 22 0 this Lnew_package/jvm/p46/Test4;
0 22 1 x I
}
SourceFile: "Test4.java"