性能文章>12 给对象添加轻量级锁的调试>

12 给对象添加轻量级锁的调试原创

9月前
218854

前言

09 给对象添加偏向锁的调试

10 偏向锁的退出的调试

11 偏向锁的重入 以及 线程1获取偏向锁并释放线程2获取锁 的调试

接着前三篇,我们这里来调试一下轻量级锁 

 

以下内容基于 jdk9 + lldb-1001.0.13.3 

另外以下运行时数据可能是来自于多次调试,可能会存在运行时数据对不上的情况, 但是条理逻辑会在文字中描述清楚的 

另外文章中还有一些疑问, 后面再来补充吧 

备注 : 以下调试增加了vm参数 : -XX:-UseBiasedLocking

测试用例

package com.hx.test04;

/**
 * SynchronizedObject
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @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获取锁 的调试

点赞收藏
分类:标签:
黄金键盘
请先登录,查看5条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步
4
5