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

时间:2018-01-27 12:43:06

标签: java multithreading jvm jit

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?

1 个答案:

答案 0 :(得分:1)

  1. 您正在查看C1编译的方法。这不是通常在热路径上运行的。只要使用C2编译main方法,就不会调用increment方法,而是将其内联到外部循环中。
  2. 您要问的一段代码是偏向锁输入序列。为了更好地理解它,您需要知道Biased Locking works的方式。 rbx此处包含对象标题(标记字)。 and $0x7, rbx指令屏蔽偏置的锁定模式位。
  3. 查看MacroAssembler::biased_locking_enter的源代码,看看发生了什么。顺便说一下,这些消息来源附有许多有价值的评论,这些评论确实有助于理解算法。