java - 了解运行时无争用的同步方法

标签 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

问题:

为什么我们有 4 个 lock cmpxchg %rbx, ($rsi) 如果我们在第一个 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. 您正在查看 C1 编译的方法。这不是通常在热路径上运行的。一旦 main 方法用 C2 编译,就不会调用 increment 方法,而是内联到外部循环中。
  2. 您要询问的这段代码是偏向锁定输入 序列。为了更好地理解它,您需要知道如何 Biased Locking works . rbx 这里包含了对象头(mark word)。 和 $0x7, rbx 指令屏蔽偏向锁模式位。
  3. 查看MacroAssembler::biased_locking_enter的源代码看看发生了什么。顺便说一句,这些来源附有许多真正有助于理解算法的宝贵评论。

关于java - 了解运行时无争用的同步方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48475922/

相关文章:

java - 如何生成与从 JVisualVM 获得的线程转储类似的线程转储?

java - 在服务验证请求的 GET 响应中返回附加参数或用户属性

java - 在java中翻转图像

Java:如何确定抽象类型的具体类型?

.net - 如何安全地处理 System.Timers.Timer?

java - 从 SBT 资源文件夹中读取文件而不转义字符

java - 打印与数组中的数字匹配的字符串

java - 使用JAVA并行流时是否可以按线程传递资源?

java - 创建和使用池化客户端 stub 时 ServiceClient.cleanupTransport() 的 axis2 NullPointerException

java - 直接分配给老年代