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)
这些都是出于学习目的,并不打算用于生产中。
我试图了解
synchronized
在JVM HotSpot上的运行时的行为。这是我尝试过的: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++;
}
}
这是整个应用程序。我有兴趣了解它在运行时如何工作。所以我用
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
我很困惑的是这个片段:
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
首先,我们比较
rbx
并将其与[rsi]
交换。由于lock cmpxchng
修改了ZF,因此我们有条件地跳转到jne 0x7f62d11129dd
(我认为这是在CAS失败的情况下)。如果CAS成功,我们将无条件跳转到jmpq 0x7f62d111295c
。问题:
如果在第一个
lock cmpxchg %rbx, ($rsi)
之后的跳转中仍然跳过它们,为什么我们有4个lock cmpxchg %rbx, ($rsi)
?UPD :我想我理解我们为什么需要此跳转。首先没有注意到这些说明:
0x00007f62d11128d1: test $0x7,%rbx
0x00007f62d11128d8: jne 0x7f62d1112921
0x00007f62d11128da: test $0x300,%rbx
0x00007f62d11128e1: jne 0x7f62d1112900
当时还不清楚
rbx
包含什么。我看着一开始。这里是: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
方法后,就不会再调用increment
方法了,它将直接内联到外部循环中。 rbx
此处包含对象 header (标记词)。 and $0x7, rbx
指令掩盖有偏见的锁定模式位。 MacroAssembler::biased_locking_enter
的源代码以了解发生了什么。顺便说一句,这些资源附带了许多有值(value)的评论,这些评论确实有助于理解算法。