[英]Understanding synchronized method at runtime with no contention
java version "1.8.0_144"
Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)
This all is for learning purpose and not intended to be applied in production. 这一切都是出于学习目的,而不是用于生产。
I'm trying to understand how synchronized
behaves at runtime on JVM HotSpot. 我试图了解JVM HotSpot上运行时的
synchronized
行为。 Here is what I tried: 这是我尝试过的:
public class App {
public static int i = 0;
public static void main(String[] args) {
for(int i = 0; i < 10000; i++){
increment();
}
if(new Object().hashCode() != i){
System.out.print("");
}
}
static synchronized void increment(){
i++;
}
}
This is the whole application. 这是整个应用程序。 I was interested to understand how this will work at runtime.
我有兴趣了解它如何在运行时工作。 So I disassembled with
java -server -XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*App.increment -jar target/test-1.0.0.jar
and got this : 所以我用
java -server -XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*App.increment -jar target/test-1.0.0.jar
反汇编java -server -XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*App.increment -jar target/test-1.0.0.jar
得到了这个:
# {method} {0x00007f62e5693438} 'increment' '()V' in 'com/test/App'
# [sp+0x50] (sp of caller)
0x00007f62d1112880: mov %eax,0xfffffffffffec000(%rsp)
0x00007f62d1112887: push %rbp
0x00007f62d1112888: sub $0x40,%rsp
0x00007f62d111288c: movabs $0xd6f77a00,%rsi ; {oop(a 'java/lang/Class' = 'com/test/App')}
0x00007f62d1112896: lea 0x20(%rsp),%rdi
0x00007f62d111289b: mov %rsi,0x8(%rdi)
0x00007f62d111289f: mov (%rsi),%rax
0x00007f62d11128a2: mov %rax,%rbx
0x00007f62d11128a5: and $0x7,%rbx
0x00007f62d11128a9: cmp $0x5,%rbx
0x00007f62d11128ad: jne 0x7f62d1112934
0x00007f62d11128b3: mov 0x8(%rsi),%ebx
0x00007f62d11128b6: shl $0x3,%rbx
0x00007f62d11128ba: mov 0xa8(%rbx),%rbx
0x00007f62d11128c1: or %r15,%rbx
0x00007f62d11128c4: xor %rax,%rbx
0x00007f62d11128c7: and $0xffffffffffffff87,%rbx
0x00007f62d11128cb: je 0x7f62d111295c
0x00007f62d11128d1: test $0x7,%rbx
0x00007f62d11128d8: jne 0x7f62d1112921
0x00007f62d11128da: test $0x300,%rbx
0x00007f62d11128e1: jne 0x7f62d1112900
0x00007f62d11128e3: and $0x37f,%rax
0x00007f62d11128ea: mov %rax,%rbx
0x00007f62d11128ed: or %r15,%rbx
0x00007f62d11128f0: lock cmpxchg %rbx,(%rsi)
0x00007f62d11128f5: jne 0x7f62d11129dd
0x00007f62d11128fb: jmpq 0x7f62d111295c
0x00007f62d1112900: mov 0x8(%rsi),%ebx
0x00007f62d1112903: shl $0x3,%rbx
0x00007f62d1112907: mov 0xa8(%rbx),%rbx
0x00007f62d111290e: or %r15,%rbx
0x00007f62d1112911: lock cmpxchg %rbx,(%rsi)
0x00007f62d1112916: jne 0x7f62d11129dd
0x00007f62d111291c: jmpq 0x7f62d111295c
0x00007f62d1112921: mov 0x8(%rsi),%ebx
0x00007f62d1112924: shl $0x3,%rbx
0x00007f62d1112928: mov 0xa8(%rbx),%rbx
0x00007f62d111292f: lock cmpxchg %rbx,(%rsi)
0x00007f62d1112934: mov (%rsi),%rax
0x00007f62d1112937: or $0x1,%rax
0x00007f62d111293b: mov %rax,(%rdi)
0x00007f62d111293e: lock cmpxchg %rdi,(%rsi)
0x00007f62d1112943: je 0x7f62d111295c
0x00007f62d1112949: sub %rsp,%rax
0x00007f62d111294c: and $0xfffffffffffff007,%rax
0x00007f62d1112953: mov %rax,(%rdi)
0x00007f62d1112956: jne 0x7f62d11129dd
0x00007f62d111295c: movabs $0x7f62e5693668,%rax ; {metadata(method data for {method} {0x00007f62e5693438} 'increment' '()V' in 'com/test/App')}
0x00007f62d1112966: mov 0xdc(%rax),%esi
0x00007f62d111296c: add $0x8,%esi
0x00007f62d111296f: mov %esi,0xdc(%rax)
0x00007f62d1112975: movabs $0x7f62e5693438,%rax ; {metadata({method} {0x00007f62e5693438} 'increment' '()V' in 'com/test/App')}
0x00007f62d111297f: and $0x1ff8,%esi
0x00007f62d1112985: cmp $0x0,%esi
0x00007f62d1112988: je 0x7f62d11129f0
0x00007f62d111298e: movabs $0xd6f77a00,%rax ; {oop(a 'java/lang/Class' = 'com/test/App')}
0x00007f62d1112998: mov 0x68(%rax),%esi ;*getstatic i
; - com.test.App::increment@0 (line 19)
0x00007f62d111299b: incl %esi
0x00007f62d111299d: mov %esi,0x68(%rax) ;*putstatic i
; - com.test.App::increment@5 (line 19)
0x00007f62d11129a0: lea 0x20(%rsp),%rax
0x00007f62d11129a5: mov 0x8(%rax),%rdi
0x00007f62d11129a9: mov (%rdi),%rsi
0x00007f62d11129ac: and $0x7,%rsi
0x00007f62d11129b0: cmp $0x5,%rsi
0x00007f62d11129b4: je 0x7f62d11129d1
0x00007f62d11129ba: mov (%rax),%rsi
0x00007f62d11129bd: test %rsi,%rsi
0x00007f62d11129c0: je 0x7f62d11129d1
0x00007f62d11129c6: lock cmpxchg %rsi,(%rdi)
0x00007f62d11129cb: jne 0x7f62d1112a04 ;*return
; - com.test.App::increment@8 (line 20)
0x00007f62d11129d1: add $0x40,%rsp
0x00007f62d11129d5: pop %rbp
0x00007f62d11129d6: test %eax,0x18b4c724(%rip) ; {poll_return}
0x00007f62d11129dc: retq
0x00007f62d11129dd: mov %rsi,0x8(%rsp)
0x00007f62d11129e2: mov %rdi,(%rsp)
0x00007f62d11129e6: callq 0x7f62d1105420 ; OopMap{rsi=Oop [40]=Oop off=363}
;*synchronization entry
; - com.test.App::increment@-1 (line 19)
; {runtime_call}
0x00007f62d11129eb: jmpq 0x7f62d111295c
0x00007f62d11129f0: mov %rax,0x8(%rsp)
0x00007f62d11129f5: movq $0xffffffffffffffff,(%rsp)
0x00007f62d11129fd: callq 0x7f62d1106fa0 ; OopMap{[40]=Oop off=386}
;*synchronization entry
; - com.test.App::increment@-1 (line 19)
; {runtime_call}
0x00007f62d1112a02: jmp 0x7f62d111298e
0x00007f62d1112a04: lea 0x20(%rsp),%rax
0x00007f62d1112a09: mov %rax,(%rsp)
0x00007f62d1112a0d: callq 0x7f62d11057e0 ; {runtime_call}
0x00007f62d1112a12: jmp 0x7f62d11129d1
0x00007f62d1112a14: nop
0x00007f62d1112a15: nop
0x00007f62d1112a16: mov 0x2a8(%r15),%rax
0x00007f62d1112a1d: movabs $0x0,%r10
0x00007f62d1112a27: mov %r10,0x2a8(%r15)
0x00007f62d1112a2e: movabs $0x0,%r10
0x00007f62d1112a38: mov %r10,0x2b0(%r15)
0x00007f62d1112a3f: mov %rax,%rbx
0x00007f62d1112a42: lea 0x20(%rsp),%rax
0x00007f62d1112a47: mov 0x8(%rax),%rsi
0x00007f62d1112a4b: mov (%rsi),%rdi
0x00007f62d1112a4e: and $0x7,%rdi
0x00007f62d1112a52: cmp $0x5,%rdi
0x00007f62d1112a56: je 0x7f62d1112a73
0x00007f62d1112a5c: mov (%rax),%rdi
0x00007f62d1112a5f: test %rdi,%rdi
0x00007f62d1112a62: je 0x7f62d1112a73
0x00007f62d1112a68: lock cmpxchg %rdi,(%rsi)
0x00007f62d1112a6d: jne 0x7f62d1112a80
0x00007f62d1112a73: mov %rbx,%rax
0x00007f62d1112a76: add $0x40,%rsp
0x00007f62d1112a7a: pop %rbp
0x00007f62d1112a7b: jmpq 0x7f62d10755a0 ; {runtime_call}
0x00007f62d1112a80: lea 0x20(%rsp),%rax
0x00007f62d1112a85: mov %rax,(%rsp)
0x00007f62d1112a89: callq 0x7f62d11057e0 ; {runtime_call}
0x00007f62d1112a8e: jmp 0x7f62d1112a73
0x00007f62d1112a90: hlt
0x00007f62d1112a91: hlt
0x00007f62d1112a92: hlt
0x00007f62d1112a93: hlt
0x00007f62d1112a94: hlt
0x00007f62d1112a95: hlt
0x00007f62d1112a96: hlt
0x00007f62d1112a97: hlt
0x00007f62d1112a98: hlt
0x00007f62d1112a99: hlt
0x00007f62d1112a9a: hlt
0x00007f62d1112a9b: hlt
0x00007f62d1112a9c: hlt
0x00007f62d1112a9d: hlt
0x00007f62d1112a9e: hlt
0x00007f62d1112a9f: hlt
The thing I was confused by is this fragment: 我被困惑的是这个片段:
0x00007f62d11128f0: lock cmpxchg %rbx,(%rsi) ;1st cmpxchg
0x00007f62d11128f5: jne 0x7f62d11129dd
0x00007f62d11128fb: jmpq 0x7f62d111295c
0x00007f62d1112900: mov 0x8(%rsi),%ebx
0x00007f62d1112903: shl $0x3,%rbx
0x00007f62d1112907: mov 0xa8(%rbx),%rbx
0x00007f62d111290e: or %r15,%rbx
0x00007f62d1112911: lock cmpxchg %rbx,(%rsi) ;2nd cmpxchg
0x00007f62d1112916: jne 0x7f62d11129dd
0x00007f62d111291c: jmpq 0x7f62d111295c
0x00007f62d1112921: mov 0x8(%rsi),%ebx
0x00007f62d1112924: shl $0x3,%rbx
0x00007f62d1112928: mov 0xa8(%rbx),%rbx
0x00007f62d111292f: lock cmpxchg %rbx,(%rsi) ;3rd cmpxchg
0x00007f62d1112934: mov (%rsi),%rax
0x00007f62d1112937: or $0x1,%rax
0x00007f62d111293b: mov %rax,(%rdi)
0x00007f62d111293e: lock cmpxchg %rdi,(%rsi) ;4th cmpxchg
0x00007f62d1112943: je 0x7f62d111295c
0x00007f62d1112949: sub %rsp,%rax
0x00007f62d111294c: and $0xfffffffffffff007,%rax
0x00007f62d1112953: mov %rax,(%rdi)
0x00007f62d1112956: jne 0x7f62d11129dd
First we compare and exchange rbx
with [rsi]
. 首先,我们将
rbx
与[rsi]
进行比较和交换。 Since lock cmpxchng
modifies ZF we conditionally jump to jne 0x7f62d11129dd
(I think this is in case of CAS failure). 由于
lock cmpxchng
修改了ZF,我们有条件地跳转到jne 0x7f62d11129dd
(我认为这是在CAS失败的情况下)。 In case of CAS succeed we jump unconditionally to jmpq 0x7f62d111295c
. 如果CAS成功,我们无条件地跳转到
jmpq 0x7f62d111295c
。
QUESTION: 题:
Why do we have 4 lock cmpxchg %rbx, ($rsi)
if we skip them anyway in the jumps after the first lock cmpxchg %rbx, ($rsi)
? 为什么我们有4个
lock cmpxchg %rbx, ($rsi)
如果我们在第一次lock cmpxchg %rbx, ($rsi)
之后的跳转中跳过它们?
UPD : I think I understood why we needed this jumps. UPD :我想我理解为什么我们需要这种跳跃。 Did not noticed these instructions first:
先没有注意到这些说明:
0x00007f62d11128d1: test $0x7,%rbx
0x00007f62d11128d8: jne 0x7f62d1112921
0x00007f62d11128da: test $0x300,%rbx
0x00007f62d11128e1: jne 0x7f62d1112900
The thing is still not quite clear is what rbx
contains at that time. 事情还不太清楚当时
rbx
包含的内容。 I looked at the very beginning. 我一开始就看着。 Here it is:
这里是:
0x00007f62d111288c: movabs $0xd6f77a00,%rsi ; seems the object data
;...
0x00007f62d111289b: mov %rsi,0x8(%rdi) ; Header offset?
0x00007f62d111289f: mov (%rsi),%rax
0x00007f62d11128a2: mov %rax,%rbx
0x00007f62d11128a5: and $0x7,%rbx ; Why do we do this and?
main
method is compiled with C2, there will be no call to increment
method, it will be rather inlined into the outer loop. main
方法,就不会调用increment
方法,而是将其内联到外部循环中。 rbx
here contains the object header (mark word). rbx
包含对象标题(标记字)。 and $0x7, rbx
instruction masks biased lock pattern bits. and $0x7, rbx
指令掩盖偏置的锁模式位。 MacroAssembler::biased_locking_enter
to see what's going on. MacroAssembler::biased_locking_enter
的源代码,看看发生了什么。 By the way, the sources are accompanied with many valuable comments that really help in understanding the algorithm.
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.