12 給對象添加輕量級鎖的調試

前言

09 給對象添加偏向鎖的調試

10 偏向鎖的退出的調試

11 偏向鎖的重入 以及 線程1獲取偏向鎖並釋放線程2獲取鎖 的調試

呵呵 接着前三篇 

我們這裏來調試一下 輕量級鎖 

 

一下內容基於 jdk9 + lldb-1001.0.13.3 

另外一下 運行時數據可能是來自於多次調試, 可能會存在運行時數據 對不上的情況, 但是的條理邏輯會在文字中描述清楚的 

另外文章中還有一些疑問, 呵呵 後面再來補充吧 

備註 : 以下調試 增加了vm參數 : -XX:-UseBiasedLocking

 

 

測試用例

package com.hx.test04;

/**
 * SynchronizedObject
 *
 * @author Jerry.X.He <[email protected]>
 * @version 1.0
 * @date 2020-04-03 15:14
 */
public class Test26SynchronizeObject implements Cloneable {

  // identStr
  private String identStr = "xyz";
  int f01;
  int f02;
  int f03;
  int f04;
  int f05;

  // Test25SynchronizeObject
  public static void main(String[] args) throws Exception {

    Test26SynchronizeObject lockObj = new Test26SynchronizeObject();

    synchronized (lockObj) {

//      Test26SynchronizeObject cloned = (Test26SynchronizeObject) lockObj.clone();
//      System.out.println(lockObj.identStr);

    }

  }

}

 

對應的字節碼信息如下, 下面參照可能需要使用到 

master:classes jerry$ javap -c com/hx/test04/Test26SynchronizeObject.class 
Compiled from "Test26SynchronizeObject.java"
public class com.hx.test04.Test26SynchronizeObject implements java.lang.Cloneable {
  int f01;

  int f02;

  int f03;

  int f04;

  int f05;

  public com.hx.test04.Test26SynchronizeObject();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: aload_0
       5: ldc           #2                  // String xyz
       7: putfield      #3                  // Field identStr:Ljava/lang/String;
      10: return

  public static void main(java.lang.String[]) throws java.lang.Exception;
    Code:
       0: new           #4                  // class com/hx/test04/Test26SynchronizeObject
       3: dup
       4: invokespecial #5                  // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: dup
      10: astore_2
      11: monitorenter
      12: aload_2
      13: monitorexit
      14: goto          22
      17: astore_3
      18: aload_2
      19: monitorexit
      20: aload_3
      21: athrow
      22: return
    Exception table:
       from    to  target type
          12    14    17   any
          17    20    17   any
}

 

 

添加輕量級鎖的調試 

啓動命令如下, 爲了調試方便 關閉偏向鎖 

lldb -s /Users/jerry/LLDBProjects/Test26SynchronizeObjectDebug.sh -- ./java -cp /Users/jerry/IdeaProjects/HelloWorld/target/classes -XX:BiasedLockingStartupDelay=0 -XX:-UseBiasedLocking com.hx.test04.Test26SynchronizeObject

 

添加輕量級鎖的調試信息如下 

(lldb) p _active_table._table[9][194]
(address) $0 = 0x000000010684f600 "XH;"
(lldb) b 0x000000010684f600
Breakpoint 3: address = 0x000000010684f600
(lldb) c
Process 883 resuming
Process 883 stopped
(lldb) re r
General Purpose Registers:
       rax = 0x0000000747bb86a0
       rbx = 0x00000000000000c2
       rcx = 0x0000000000000008
       rdx = 0x0000000747bb86c0
       rdi = 0x0000000102001c20
       rsi = 0x000070000debc5d0
       rbp = 0x000070000debc680
       rsp = 0x000070000debc630
        r8 = 0x0000000000000000
        r9 = 0x0000000000000020
       r10 = 0x0000000104b0b270  libjvm.dylib`TemplateInterpreter::_active_table + 18432
       r11 = 0x00006fff0beba800
       r12 = 0x0000000000000000
       r13 = 0x000000011e567f93
       r14 = 0x000070000debc6a8
       r15 = 0x0000000102001c20
       rip = 0x000000010684f600
    rflags = 0x0000000000000206
        cs = 0x000000000000002b
        fs = 0x0000000000000000
        gs = 0x0000000000000000

(lldb) p ((oopDesc*)0x0000000747bb86a0)->print()
com.hx.test04.Test26SynchronizeObject
{0x0000000747bb86a0} - klass: 'com/hx/test04/Test26SynchronizeObject'
 - ---- fields (total size 5 words):
 - 'f01' 'I' @12  0
 - 'f02' 'I' @16  0
 - 'f03' 'I' @20  0
 - 'f04' 'I' @24  0
 - 'f05' 'I' @28  0
 - private 'identStr' 'Ljava/lang/String;' @32  "xyz"{0x0000000747bb86c8} (e8f770d9 0)
(lldb) p ((oopDesc*)0x0000000747bb86a0)->mark()->has_locker()
(bool) $1 = false
(lldb) p ((oopDesc*)0x0000000747bb86a0)->mark()->has_bias_pattern()
(bool) $2 = false
(lldb) x 0x0000000747bb86a0
0x747bb86a0: 01 00 00 00 00 00 00 00 86 1f 01 f8 00 00 00 00  ...........?....
0x747bb86b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
(lldb) b 0x10684f663
Breakpoint 4: address = 0x000000010684f663
(lldb) c
Process 883 resuming
Process 883 stopped
* thread #5, stop reason = breakpoint 4.1
    frame #0: 0x000000010684f663
->  0x10684f663: movq   0x8(%rsi), %rcx
    0x10684f667: movl   $0x1, %eax
    0x10684f66c: orq    (%rcx), %rax
    0x10684f66f: movq   %rax, (%rsi)
Target 0: (java) stopped.
(lldb) stepi
Process 883 stopped
* thread #5, stop reason = instruction step into
    frame #0: 0x000000010684f667
->  0x10684f667: movl   $0x1, %eax
    0x10684f66c: orq    (%rcx), %rax
    0x10684f66f: movq   %rax, (%rsi)
    0x10684f672: lock
Target 0: (java) stopped.
(lldb) stepi
Process 883 stopped
* thread #5, stop reason = instruction step into
    frame #0: 0x000000010684f66c
->  0x10684f66c: orq    (%rcx), %rax
    0x10684f66f: movq   %rax, (%rsi)
    0x10684f672: lock
    0x10684f673: cmpxchgq %rsi, (%rcx)
Target 0: (java) stopped.
(lldb) stepi
Process 883 stopped
* thread #5, stop reason = instruction step into
    frame #0: 0x000000010684f66f
->  0x10684f66f: movq   %rax, (%rsi)
    0x10684f672: lock
    0x10684f673: cmpxchgq %rsi, (%rcx)
    0x10684f677: je     0x10684f8f0
Target 0: (java) stopped.
(lldb) stepi
Process 883 stopped
* thread #5, stop reason = instruction step into
    frame #0: 0x000000010684f672
->  0x10684f672: lock
    0x10684f673: cmpxchgq %rsi, (%rcx)
    0x10684f677: je     0x10684f8f0
    0x10684f67d: subq   %rsp, %rax
Target 0: (java) stopped.
(lldb) stepi
Process 883 stopped
* thread #5, stop reason = instruction step into
    frame #0: 0x000000010684f677
->  0x10684f677: je     0x10684f8f0
    0x10684f67d: subq   %rsp, %rax
    0x10684f680: andq   $-0xff9, %rax             ; imm = 0xF007
    0x10684f687: movq   %rax, (%rsi)
Target 0: (java) stopped.
(lldb) stepi
Process 883 stopped
* thread #5, stop reason = instruction step into
    frame #0: 0x000000010684f8f0
->  0x10684f8f0: movq   %r13, -0x40(%rbp)
    0x10684f8f4: movl   %eax, -0x16000(%rsp)
    0x10684f8fb: movzbl (%r13), %ebx
    0x10684f900: movabsq $0x104b0b270, %r10        ; imm = 0x104B0B270
Target 0: (java) stopped.
(lldb) p ((oopDesc*)0x0000000747bb86a0)->print()
com.hx.test04.Test26SynchronizeObject
{0x0000000747bb86a0} - klass: 'com/hx/test04/Test26SynchronizeObject'
 - ---- fields (total size 5 words):
 - 'f01' 'I' @12  0
 - 'f02' 'I' @16  0
 - 'f03' 'I' @20  0
 - 'f04' 'I' @24  0
 - 'f05' 'I' @28  0
 - private 'identStr' 'Ljava/lang/String;' @32  "xyz"{0x0000000747bb86c8} (e8f770d9 0)
(lldb) p ((oopDesc*)0x0000000747bb86a0)->mark()->has_locker()
(bool) $3 = true
(lldb) p ((oopDesc*)0x0000000747bb86a0)->mark()->locker()
(BasicLock *) $4 = 0x000070000debc628
(lldb) p ((oopDesc*)0x0000000747bb86a0)->mark()->has_bias_pattern()
(bool) $5 = false
(lldb) x 0x000070000debc628
0x70000debc628: 01 00 00 00 00 00 00 00 a0 86 bb 47 07 00 00 00  ........?.?G....
0x70000debc638: 28 c6 eb 0d 00 70 00 00 8c 7f 56 1e 01 00 00 00  (??..p....V.....

以上調試信息沒有體現出來的是 加了 -XX:-UseBiasedLocking 禁用偏向鎖的配置之後, 彙編代碼裏面 增加偏向鎖處理的代碼片段滅有了, 具體的原因 可以參見 09 給對象添加偏向鎖的調試 

再看看這裏, monitorenter 剛進入的時候, 因爲禁用了偏向鎖, 因此對象頭 鎖標記是 0x01, 是否允許偏向鎖是 0x0, 然後 也沒有輕量級鎖[has_locker] 

然後 到將 BasicObjectLock cas 替換掉對象頭之後, lockObj 的輕量級鎖就添加上了[has_locker], 另外就是 lockObj 對象頭裏面存放了 BasicObjectLock 的地址信息 

通過地址信息可以查看 BasicObjectLock 的 lock 和 obj 的數據 

 

 

添加輕量級鎖是否丟失了age?

(lldb) p _active_table._table[9][194]
(address) $0 = 0x000000010604f600 "XH;"
(lldb) b 0x000000010604f600
Breakpoint 3: address = 0x000000010604f600
(lldb) c
Process 906 resuming
Process 906 stopped
* thread #5, stop reason = breakpoint 3.1
    frame #0: 0x000000010604f600
->  0x10604f600: popq   %rax
    0x10604f601: cmpq   (%rax), %rax
    0x10604f604: xorl   %esi, %esi
    0x10604f606: movq   -0x48(%rbp), %rcx
Target 0: (java) stopped.
(lldb) re r
General Purpose Registers:
       rax = 0x0000000747bb85e8
       rbx = 0x00000000000000c2
       rcx = 0x0000000000000008
       rdx = 0x0000000747bb8608
       rdi = 0x0000000105001c20
       rsi = 0x00007000039b65d0
       rbp = 0x00007000039b6680
       rsp = 0x00007000039b6630
        r8 = 0x0000000000000000
        r9 = 0x0000000000000020
       r10 = 0x000000010430b270  libjvm.dylib`TemplateInterpreter::_active_table + 18432
       r11 = 0x00006ffefe9b4800
       r12 = 0x0000000000000000
       r13 = 0x000000011db69f93
       r14 = 0x00007000039b66a8
       r15 = 0x0000000105001c20
       rip = 0x000000010604f600
    rflags = 0x0000000000000206
        cs = 0x000000000000002b
        fs = 0x0000000000000000
        gs = 0x0000000000000000

(lldb) p ((oopDesc*)0x0000000747bb85e8)->print()
com.hx.test04.Test26SynchronizeObject 
{0x0000000747bb85e8} - klass: 'com/hx/test04/Test26SynchronizeObject'
 - ---- fields (total size 5 words):
 - 'f01' 'I' @12  0
 - 'f02' 'I' @16  0
 - 'f03' 'I' @20  0
 - 'f04' 'I' @24  0
 - 'f05' 'I' @28  0
 - private 'identStr' 'Ljava/lang/String;' @32  "xyz"{0x0000000747bb8610} (e8f770c2 0)
(lldb) p ((oopDesc*)0x0000000747bb85e8)->age()
(uint) $1 = 0
(lldb) p ((oopDesc*)0x0000000747bb85e8)->incr_age()
(lldb) p ((oopDesc*)0x0000000747bb85e8)->age()
(uint) $2 = 1
(lldb) b 0x10604f677
Breakpoint 4: address = 0x000000010604f677
(lldb) c
Process 906 resuming
Process 906 stopped
* thread #5, stop reason = breakpoint 4.1
    frame #0: 0x000000010604f677
->  0x10604f677: je     0x10604f8f0
    0x10604f67d: subq   %rsp, %rax
    0x10604f680: andq   $-0xff9, %rax             ; imm = 0xF007 
    0x10604f687: movq   %rax, (%rsi)
Target 0: (java) stopped.
(lldb) stepi
Process 906 stopped
* thread #5, stop reason = instruction step into
    frame #0: 0x000000010604f8f0
->  0x10604f8f0: movq   %r13, -0x40(%rbp)
    0x10604f8f4: movl   %eax, -0x16000(%rsp)
    0x10604f8fb: movzbl (%r13), %ebx
    0x10604f900: movabsq $0x10430b270, %r10        ; imm = 0x10430B270 
Target 0: (java) stopped.
(lldb) p ((oopDesc*)0x0000000747bb85e8)->age()
(uint) $3 = 1

由實際的測試可知, 添加了 輕量級鎖 之後 age 的信息是沒有丟失的, 那麼這是爲什麼呢?, lockObj 的對象頭被替換成了 BasicObjectLock 

 

查看下 oop.inline.hpp 裏面的 age 的代碼, 如果是有 displaced_header, 則使用 displaced_header 獲取年齡 

// The following method needs to be MT safe.
uint oopDesc::age() const {
  assert(!is_forwarded(), "Attempt to read age from forwarded mark");
  if (has_displaced_mark()) {
    return displaced_mark()->age();
  } else {
    return mark()->age();
  }
}

 

 

輕量級鎖的釋放

(lldb) p _active_table._table[9][194]
(address) $0 = 0x000000010584f600 "XH;"
(lldb) p _active_table._table[9][195]
(address) $1 = 0x000000010584f9e0 "XH;"
(lldb) b 0x000000010584f9e1
Breakpoint 3: address = 0x000000010584f9e1
(lldb) c
Process 923 resuming
Process 923 stopped
* thread #5, stop reason = breakpoint 3.1
    frame #0: 0x000000010584f9e1
->  0x10584f9e1: cmpq   (%rax), %rax
    0x10584f9e4: movq   -0x48(%rbp), %rsi
    0x10584f9e8: leaq   -0x48(%rbp), %rdx
    0x10584f9ec: jmp    0x10584f9fc
Target 0: (java) stopped.
(lldb) re r
General Purpose Registers:
       rax = 0x0000000747bb86a0
       rbx = 0x00000000000000c3
       rcx = 0x0000000747bb86a0
       rdx = 0x0000700004381638
       rdi = 0x0000000105002620
       rsi = 0x0000700004381628
       rbp = 0x0000700004381680
       rsp = 0x0000700004381628
        r8 = 0x0000000000000000
        r9 = 0x0000000000000020
       r10 = 0x000000010430aa70  libjvm.dylib`TemplateInterpreter::_active_table + 16384
       r11 = 0x0000000000000200
       r12 = 0x0000000000000000
       r13 = 0x000000011c7f1f95
       r14 = 0x00007000043816a8
       r15 = 0x0000000105002620
       rip = 0x000000010584f9e1
    rflags = 0x0000000000000206
        cs = 0x000000000000002b
        fs = 0x0000000000000000
        gs = 0x0000000000000000

(lldb) p ((oopDesc*)0x0000000747bb86a0)->print()
com.hx.test04.Test26SynchronizeObject 
{0x0000000747bb86a0} - klass: 'com/hx/test04/Test26SynchronizeObject'
 - ---- fields (total size 5 words):
 - 'f01' 'I' @12  0
 - 'f02' 'I' @16  0
 - 'f03' 'I' @20  0
 - 'f04' 'I' @24  0
 - 'f05' 'I' @28  0
 - private 'identStr' 'Ljava/lang/String;' @32  "xyz"{0x0000000747bb86c8} (e8f770d9 0)
(lldb) p ((oopDesc*)0x0000000747bb86a0)->has_locker()
error: no member named 'has_locker' in 'oopDesc'
(lldb) p ((oopDesc*)0x0000000747bb86a0)->mark()->has_locker()
(bool) $2 = true
(lldb) p ((oopDesc*)0x0000000747bb86a0)->mark()->locker()
(BasicLock *) $3 = 0x0000700004381628
(lldb) x 0x0000000747bb86a0
0x747bb86a0: 28 16 38 04 00 70 00 00 86 1f 01 f8 00 00 00 00  (.8..p.....?....
0x747bb86b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
(lldb) x 0x0000700004381628
0x700004381628: 01 00 00 00 00 00 00 00 a0 86 bb 47 07 00 00 00  ........?.?G....
0x700004381638: 28 16 38 04 00 70 00 00 94 1f 7f 1c 01 00 00 00  (.8..p..........
(lldb) b 0x10584fcdd
Breakpoint 4: address = 0x000000010584fcdd
(lldb) c
Process 923 resuming
Process 923 stopped
* thread #5, stop reason = breakpoint 4.1
    frame #0: 0x000000010584fcdd
->  0x10584fcdd: leaq   (%rsi), %rax
    0x10584fce0: movq   0x8(%rsi), %rcx
    0x10584fce4: movq   $0x0, 0x8(%rsi)
    0x10584fcec: movq   (%rax), %rdx
Target 0: (java) stopped.
(lldb) re r
General Purpose Registers:
       rax = 0x0000000747bb86a0
       rbx = 0x00000000000000c3
       rcx = 0x0000000747bb86a0
       rdx = 0x0000700004381638
       rdi = 0x0000000105002620
       rsi = 0x0000700004381628
       rbp = 0x0000700004381680
       rsp = 0x0000700004381620
        r8 = 0x0000000000000000
        r9 = 0x0000000000000020
       r10 = 0x000000010430aa70  libjvm.dylib`TemplateInterpreter::_active_table + 16384
       r11 = 0x0000000000000200
       r12 = 0x0000000000000000
       r13 = 0x000000011c7f1f95
       r14 = 0x00007000043816a8
       r15 = 0x0000000105002620
       rip = 0x000000010584fcdd
    rflags = 0x0000000000000246
        cs = 0x000000000000002b
        fs = 0x0000000000000000
        gs = 0x0000000000000000

(lldb) stepi -c 3
Process 923 stopped
* thread #5, stop reason = instruction step into
    frame #0: 0x000000010584fcec
->  0x10584fcec: movq   (%rax), %rdx
    0x10584fcef: testq  %rdx, %rdx
    0x10584fcf2: je     0x10584ff67
    0x10584fcf8: lock   
Target 0: (java) stopped.
(lldb) stepi -c 2
Process 923 stopped
* thread #5, stop reason = instruction step into
    frame #0: 0x000000010584fcf2
->  0x10584fcf2: je     0x10584ff67
    0x10584fcf8: lock   
    0x10584fcf9: cmpxchgq %rdx, (%rcx)
    0x10584fcfd: je     0x10584ff67
Target 0: (java) stopped.
(lldb) stepi 
Process 923 stopped
* thread #5, stop reason = instruction step into
    frame #0: 0x000000010584fcf8
->  0x10584fcf8: lock   
    0x10584fcf9: cmpxchgq %rdx, (%rcx)
    0x10584fcfd: je     0x10584ff67
    0x10584fd03: movq   %rcx, 0x8(%rsi)
Target 0: (java) stopped.
(lldb) stepi
Process 923 stopped
* thread #5, stop reason = instruction step into
    frame #0: 0x000000010584fcfd
->  0x10584fcfd: je     0x10584ff67
    0x10584fd03: movq   %rcx, 0x8(%rsi)
    0x10584fd07: callq  0x10584fd11
    0x10584fd0c: jmp    0x10584ff67
Target 0: (java) stopped.
(lldb) re r
General Purpose Registers:
       rax = 0x0000700004381628
       rbx = 0x00000000000000c3
       rcx = 0x0000000747bb86a0
       rdx = 0x0000000000000001
       rdi = 0x0000000105002620
       rsi = 0x0000700004381628
       rbp = 0x0000700004381680
       rsp = 0x0000700004381620
        r8 = 0x0000000000000000
        r9 = 0x0000000000000020
       r10 = 0x000000010430aa70  libjvm.dylib`TemplateInterpreter::_active_table + 16384
       r11 = 0x0000000000000200
       r12 = 0x0000000000000000
       r13 = 0x000000011c7f1f95
       r14 = 0x00007000043816a8
       r15 = 0x0000000105002620
       rip = 0x000000010584fcfd
    rflags = 0x0000000000000246
        cs = 0x000000000000002b
        fs = 0x0000000000000000
        gs = 0x0000000000000000

(lldb) stepi
Process 923 stopped
* thread #5, stop reason = instruction step into
    frame #0: 0x000000010584ff67
->  0x10584ff67: movq   -0x40(%rbp), %r13
    0x10584ff6b: popq   %rax
    0x10584ff6c: movzbl 0x1(%r13), %ebx
    0x10584ff71: incq   %r13
Target 0: (java) stopped.
(lldb) p ((oopDesc*)0x0000000747bb86a0)->mark()->has_locker()
(bool) $4 = false
(lldb) 

在 monitorexit 的地方打上一個斷點 

查看 lockObj, 已經被當前線程[main]添加了 輕量級鎖, 然後 對象頭裏面存儲的是 BasicObjectLock 的地址 

BasicObjectLock 裏面存儲的是 lock, obj 一個是 lockObj 的原來的對象頭, 一個是 lockObj 的地址信息 

然後 執行 取消輕量級鎖的相關代碼, cas 將 lockObj 的對象頭替換回  displaced_header, 解鎖的操作就完成了 

再來查看 lockObj, 輕量級鎖 已經被釋放了  

 

 

進入輕量級鎖的需求?

1. UseHeavyMonitors 爲 false, 從上面的代碼可以看出 如果是 UseHeavyMonitors 爲true, monitor 只會生成走 InterpreterRuntime::monitorenter 的代碼 

2. UseBiasedLocking 爲 false, 會直接嘗試走 輕量級鎖 

3. UseBiasedLocking 爲 true, cas 偏向鎖的時候不成功, 走 InterpreterRuntime::monitorenter, 進而 可能走 輕量級鎖 

 

 

完 

 

 

參考

09 給對象添加偏向鎖的調試

10 偏向鎖的退出的調試

11 偏向鎖的重入 以及 線程1獲取偏向鎖並釋放線程2獲取鎖 的調試

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章