简体   繁体   English

在运行时了解同步方法,没有争用

[英]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?
  1. You are looking at the C1-compiled method. 您正在查看C1编译的方法。 This is not what usually runs on the hot path. 这不是通常在热路径上运行的。 As soon as main method is compiled with C2, there will be no call to increment method, it will be rather inlined into the outer loop. 一旦使用C2编译main方法,就不会调用increment方法,而是将其内联到外部循环中。
  2. The piece of code you are asking about is the biased lock enter sequence. 您要问的一段代码是偏向锁输入序列。 In order to understand it well, you'll need to know how Biased Locking works . 为了更好地理解它,您需要知道偏置锁定的工作原理 rbx here contains the object header (mark word). 这里的rbx包含对象标题(标记字)。 and $0x7, rbx instruction masks biased lock pattern bits. and $0x7, rbx指令掩盖偏置的锁模式位。
  3. Look at the source code of 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM