前言
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, 进而可能走轻量级锁