前言
锁 这块是 大家在平常的业务开发中基本上都会用到的知识
大家 平常使用到 synchronized 的时候一定是对它的使用 有一定的了解了, 那么 我们这里来调试一下 其中添加偏向锁的这个流程
以下的调试内容 仅仅是这块的相关知识的冰山一角, 我能够窥到这一角 也算是很幸运了, 还是 花了一些时间 来整理这一角的相关东西, 并且调试 相当不容易 ...
lldb 汇编调试的部分需要感谢朋友 "新加坡买买提", 去年 12 月份的时候花了一下午的时间不佞帮助我
以下内容基于 jdk9 + lldb-1001.0.13.3
另外一下 运行时数据可能是来自于多次调试, 可能会存在运行时数据 对不上的情况, 但是的条理逻辑会在文字中描述清楚的
另外文章中还有一些疑问,后面再来补充吧
测试用例
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_1
13: invokevirtual #6 // Method java/lang/Object.clone:()Ljava/lang/Object;
16: checkcast #4 // class com/hx/test04/Test26SynchronizeObject
19: astore_3
20: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
23: aload_1
24: getfield #3 // Field identStr:Ljava/lang/String;
27: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: aload_2
31: monitorexit
32: goto 42
35: astore 4
37: aload_2
38: monitorexit
39: aload 4
41: athrow
42: return
Exception table:
from to target type
12 32 35 any
35 39 35 any
}
以下的调试 基于 lldb 命令行, 因为 clion 里面调试似乎是存在问题
至少是在 我本地的这个环境, 使用 clion 调试会存在问题, 因此 只好是 换成了 lldb 命令行来调试
# 1. monitor_enter 的时候, eval : this.code_section->print("monitor_enter")
monitor_enter.code = 0x0000000112f57ec0 : 0x0000000112f57f23 : 0x0000000113028de0 (99 of 855840)
monitor_enter.locs = 0x0000000000000000 : 0x0000000000000000 : 0x0000000000000000 (0 of 0) point=0
# 2. 等待执行到 jvm.cpp 里面的 JVM_Clone 里面的断点
# 3. 打上汇编断点, 不行 os.die
# 1. p _active_table._table[9][194]
# 2. 不行 os.die
lldb 调试相关命令如下
// 呵呵 clion 里面不行, 可以通过基于命令行的 lldb 命令来调试,
cd /Users/jerry/ClionProjects/HelloOpenJdk/jdk9/build/macosx-x86_64-normal-serverANDclient-slowdebug/jdk/bin
lldb -s /Users/jerry/LLDBProjects/Test26SynchronizeObjectDebug.sh -- ./java -cp /Users/jerry/IdeaProjects/HelloWorld/target/classes -XX:BiasedLockingStartupDelay=0 com.hx.test04.Test26SynchronizeObject
可以看到这里有一个 -s 后面带了一个文件, 这里面记录的是 进入 lldb 调试之后, 需要执行的一部分命令
而这部分命令相对比较固定, 因此放到了一个统一的 文件里面, 由 lldb 加载执行
Test26SynchronizeObjectDebug.sh 文件内容如下, 主要是加了两个端点, 并执行到端点的地方
run
br set -f jni.cpp -l 1989
c
br set -f javaCalls.cpp -l 410
c
p _active_table._table[9][194]
进入lldb的调试
进入lldb 之后如下, 省略了一部分输出, 不过应该能够看出 当前端点是停在了 javaCalls.cpp -l 410 这里
Process 3256 resuming
Process 3256 stopped
* thread #5, stop reason = breakpoint 1.1
frame #0: 0x0000000103923592 libjvm.dylib`::jni_CallStaticVoidMethod(env=0x0000000100807228, cls=0x000000010010a2d0, methodID=0x00000001001b9870) at jni.cpp:1989:21
1986 va_start(args, methodID);
1987 JavaValue jvalue(T_VOID);
1988 JNI_ArgumentPusherVaArg ap(methodID, args);
-> 1989 jni_invoke_static(env, &jvalue, NULL, JNI_STATIC, methodID, &ap, CHECK);
1990 va_end(args);
1991 JNI_END
1992
Target 0: (java) stopped.
(lldb) br set -f javaCalls.cpp -l 410
Breakpoint 2: where = libjvm.dylib`JavaCalls::call_helper(JavaValue*, methodHandle const&, JavaCallArguments*, Thread*) + 1496 at javaCalls.cpp:410:7, address = 0x00000001038eba68
(lldb) c
Process 3256 resuming
Process 3256 stopped
* thread #5, stop reason = breakpoint 2.1
frame #0: 0x00000001038eba68 libjvm.dylib`JavaCalls::call_helper(result=0x0000700009373d48, method=0x0000700009373a48, args=0x0000700009373a90, __the_thread__=0x0000000100807000) at javaCalls.cpp:410:7
407 { JavaCallWrapper link(method, receiver, result, CHECK);
408 { HandleMark hm(thread); // HandleMark used by HandleMarkCleaner
409
-> 410 StubRoutines::call_stub()(
411 (address)&link,
412 // (intptr_t*)&(result->_value), // see NOTE above (compiler problem)
413 result_val_address, // see NOTE above (compiler problem)
Target 0: (java) stopped.
(lldb) p _active_table._table[9][194]
(address) $0 = 0x00000001048cd980 "XH;"
定位一下当前方法, 当前这里正是在调用 main 方法
(lldb) p method()->print()
{method}
- this oop: 0x000000011d170090
- method holder: 'com/hx/test04/Test26SynchronizeObject'
- constants: 0x000000011d16fc78 constant pool [62] {0x000000011d16fc78} for 'com/hx/test04/Test26SynchronizeObject' cache=0x000000011d170108
- access: 0x20000009 public static
- name: 'main'
- signature: '([Ljava/lang/String;)V'
- max stack: 3
- max locals: 5
- size of params: 1
- method size: 11
- vtable index: -2
- i2i entry: 0x00000001048a8700
- adapters: AHE@0x000000010101cce0: 0xb0000000 i2c: 0x0000000104a22460 c2i: 0x0000000104a2259a c2iUV: 0x0000000104a2256d
- compiled entry 0x0000000104a2259a
- code size: 43
- code start: 0x000000011d170020
- code end (excl): 0x000000011d17004b
- checked ex length: 1
- checked ex start: 0x000000011d17008c
- linenumber start: 0x000000011d17004b
- localvar length: 3
- localvar start: 0x000000011d170054
接着在 monitor_enter 的地方打上一个断点
(lldb) p _active_table._table[9][194]
(address) $0 = 0x000000010604f980 "XH;"
(lldb) b 0x000000010604f980
Breakpoint 3: address = 0x000000010604f980
(lldb) c
Process 3271 resuming
Process 3271 stopped
* thread #5, stop reason = breakpoint 3.1
frame #0: 0x000000010604f980
-> 0x10604f980: popq %rax
0x10604f981: cmpq (%rax), %rax
0x10604f984: xorl %esi, %esi
0x10604f986: movq -0x48(%rbp), %rcx
Target 0: (java) stopped.
(lldb) re r
General Purpose Registers:
rax = 0x0000000747bb8ad0
rbx = 0x00000000000000c2
rcx = 0x0000000000000008
rdx = 0x0000000747bb8af0
rdi = 0x0000000100803800
rsi = 0x0000700002e2b5c8
rbp = 0x0000700002e2b678
rsp = 0x0000700002e2b628
r8 = 0x0000000000000000
r9 = 0x0000000000000020
r10 = 0x0000000104c0b270 libjvm.dylib`TemplateInterpreter::_active_table + 18432
r11 = 0x00006fff0261b000
r12 = 0x0000000000000000
r13 = 0x000000011e26902b
r14 = 0x0000700002e2b6a8
r15 = 0x0000000100803800
rip = 0x000000010604f980
rflags = 0x0000000000000206
cs = 0x000000000000002b
fs = 0x0000000000000000
gs = 0x0000000000000000
(lldb) x 0x000000011e26902b
0x11e26902b: c2 2b b6 03 00 c0 00 04 4e b2 04 00 2b b4 01 00 ?+?..?..N?..+?..
0x11e26903b: b6 05 00 2c c3 a7 00 0a 3a 04 2c c3 19 04 bf b1 ?..,ç..:.,?..??
(lldb) p ((oopDesc*)0x0000000747bb8ad0)->print()
com.hx.test04.Test26SynchronizeObject
{0x0000000747bb8ad0} - 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"{0x0000000747bb8af8} (e8f7715f 0)
0xc2 表示 monitorenter, 0x2b 表示 aload_1
结合上面的字节码 很容易定位到当前是在执行 monitorenter
ax 存储的是对象 lockObj
查看一下 栈帧中的信息, 之所以要贴一下 栈帧信息, 是因为 monitorenter 里面的处理需要处理 栈帧中的一部分数据
(lldb) re r
General Purpose Registers:
rax = 0x0000000747bb8b88
rbx = 0x00000000000000c2
rcx = 0x0000000000000008
rdx = 0x0000000747bb8ba8
rdi = 0x0000000101800800
rsi = 0x000070000c6485c8
rbp = 0x000070000c648678
rsp = 0x000070000c648628
r8 = 0x0000000000000000
r9 = 0x0000000000000020
r10 = 0x0000000104b0b270 libjvm.dylib`TemplateInterpreter::_active_table + 18432
r11 = 0x00006fff0ae46800
r12 = 0x0000000000000000
r13 = 0x000000011c89102b
r14 = 0x000070000c6486a8
r15 = 0x0000000101800800
rip = 0x00000001050cd980
rflags = 0x0000000000000206
cs = 0x000000000000002b
fs = 0x0000000000000000
gs = 0x0000000000000000
(lldb) x 0x000070000c648628 -c 0x100
0x70000c648628: 88 8b bb 47 07 00 00 00 30 86 64 0c 00 70 00 00 ..?G....0.d..p..
0x70000c648638: 24 10 89 1c 01 00 00 00 a8 86 64 0c 00 70 00 00 $.......?.d..p..
0x70000c648648: 08 11 89 1c 01 00 00 00 00 00 00 00 00 00 00 00 ................
0x70000c648658: 68 86 bb 47 07 00 00 00 90 10 89 1c 01 00 00 00 h.?G............
0x70000c648668: 00 00 00 00 00 00 00 00 a8 86 64 0c 00 70 00 00 ........?.d..p..
0x70000c648678: 10 87 64 0c 00 70 00 00 f1 e9 07 05 01 00 00 00 ..d..p..??......
0x70000c648688: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x70000c648698: 88 8b bb 47 07 00 00 00 88 8b bb 47 07 00 00 00 ..?G......?G....
0x70000c6486a8: 78 8b bb 47 07 00 00 00 a0 1f 00 00 03 00 00 00 x.?G....?.......
0x70000c6486b8: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x70000c6486c8: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x70000c6486d8: 00 90 64 0c 00 70 00 00 20 88 64 0c 00 70 00 00 ..d..p.. .d..p..
0x70000c6486e8: 50 8d 64 0c 00 70 00 00 0a 00 00 00 00 70 00 00 P.d..p.......p..
0x70000c6486f8: 90 10 89 1c 01 00 00 00 00 87 0a 05 01 00 00 00 ................
0x70000c648708: a0 8a 64 0c 00 70 00 00 e0 88 64 0c 00 70 00 00 ?.d..p..?.d..p..
0x70000c648718: 1d bb 0e 04 01 00 00 00 01 00 00 00 00 70 00 00 .?...........p..
栈帧信息分析从 rsp 开始
0x70000c648628 : 0x0000000747bb8b88 : lockObj
0x70000c648630 : 0x70000c648630 : expression bottom
0x70000c648638 : 0x011c891024 : byte code pointer
0x70000c648640 : 0x70000c6486a8 : pointer to locals
0x70000c648648 : 0x011c891108 : constants pool cache
0x70000c648650 : 0x0000000000 : method data oop
0x70000c648658 : 0x0747bb8668 : java mirror
0x70000c648660 : 0x011c891090 : method oop
0x70000c648668 : 0x0000000000 : last java stack pointer
0x70000c648670 : 0x70000c6486a8 : old stack pointer
0x70000c648678 : 0x70000c648710 : old frame pointer
0x70000c648680 : 0x010507e9f1 : return address[entry_point's address]
0x70000c648688 : 0x0000000000 : ex if exists
0x70000c648690 : 0x0000000000 : cloned
0x70000c648698 : 0x0000000747bb8b88 : lockObj for monitor enter/exit
0x70000c6486a0 : 0x0000000747bb8b88 : lockObj
0x70000c6486a8 : 0x0000000747bb8b78 : args
0x70000c6486b0 : 0x0300001fa0 : $mxcsr
分配BasicObjectLock
对应 monitorenter 的汇编的模板代码如下
templateTable_x86.cpp monitorenter
// Synchronization
//
// Note: monitorenter & exit are symmetric routines; which is reflected
// in the assembly code structure as well
//
// Stack layout:
//
// [expressions ] <--- rsp = expression stack top
// ..
// [expressions ]
// [monitor entry] <--- monitor block top = expression stack bot
// ..
// [monitor entry]
// [frame data ] <--- monitor block bot
// ...
// [saved rbp ] <--- rbp
void TemplateTable::monitorenter() {
transition(atos, vtos);
// check for NULL object
__ null_check(rax);
const Address monitor_block_top(
rbp, frame::interpreter_frame_monitor_block_top_offset * wordSize);
const Address monitor_block_bot(
rbp, frame::interpreter_frame_initial_sp_offset * wordSize);
const int entry_size = frame::interpreter_frame_monitor_size() * wordSize;
Label allocated;
Register rtop = LP64_ONLY(c_rarg3) NOT_LP64(rcx);
Register rbot = LP64_ONLY(c_rarg2) NOT_LP64(rbx);
Register rmon = LP64_ONLY(c_rarg1) NOT_LP64(rdx);
// initialize entry pointer
__ xorl(rmon, rmon); // points to free slot or NULL
// find a free slot in the monitor block (result in rmon)
{
Label entry, loop, exit;
__ movptr(rtop, monitor_block_top); // points to current entry,
// starting with top-most entry
__ lea(rbot, monitor_block_bot); // points to word before bottom
// of monitor block
__ jmpb(entry);
__ bind(loop);
// check if current entry is used
__ cmpptr(Address(rtop, BasicObjectLock::obj_offset_in_bytes()), (int32_t) NULL_WORD);
// if not used then remember entry in rmon
__ cmovptr(Assembler::equal, rmon, rtop); // cmov => cmovptr
// check if current entry is for same object
__ cmpptr(rax, Address(rtop, BasicObjectLock::obj_offset_in_bytes()));
// if same object then stop searching
__ jccb(Assembler::equal, exit);
// otherwise advance to next entry
__ addptr(rtop, entry_size);
__ bind(entry);
// check if bottom reached
__ cmpptr(rtop, rbot);
// if not at bottom then check this entry
__ jcc(Assembler::notEqual, loop);
__ bind(exit);
}
__ testptr(rmon, rmon); // check if a slot has been found
__ jcc(Assembler::notZero, allocated); // if found, continue with that one
// allocate one if there's no free slot
{
Label entry, loop;
// 1. compute new pointers // rsp: old expression stack top
__ movptr(rmon, monitor_block_bot); // rmon: old expression stack bottom
__ subptr(rsp, entry_size); // move expression stack top
__ subptr(rmon, entry_size); // move expression stack bottom
__ mov(rtop, rsp); // set start value for copy loop
__ movptr(monitor_block_bot, rmon); // set new monitor block bottom
__ jmp(entry);
// 2. move expression stack contents
__ bind(loop);
__ movptr(rbot, Address(rtop, entry_size)); // load expression stack
// word from old location
__ movptr(Address(rtop, 0), rbot); // and store it at new location
__ addptr(rtop, wordSize); // advance to next word
__ bind(entry);
__ cmpptr(rtop, rmon); // check if bottom reached
__ jcc(Assembler::notEqual, loop); // if not at bottom then
// copy next word
}
// call run-time routine
// rmon: points to monitor entry
__ bind(allocated);
// Increment bcp to point to the next bytecode, so exception
// handling for async. exceptions work correctly.
// The object has already been poped from the stack, so the
// expression stack looks correct.
__ increment(rbcp);
// store object
__ movptr(Address(rmon, BasicObjectLock::obj_offset_in_bytes()), rax);
__ lock_object(rmon);
// check to make sure this monitor doesn't cause stack overflow after locking
__ save_bcp(); // in case of exception
__ generate_stack_overflow_check(0);
// The bcp has already been incremented. Just need to dispatch to
// next instruction.
__ dispatch_next(vtos);
}
monitorenter 里面 lock_object 之前对应的汇编如下
(lldb) dis -s 0x000000010604f980 -c 100
# 寄存器映射 : rmon = $esi, rtop = $rcx, rbot = $rdx
// $rax = expressionStack[0], $rsp = $expressionBottom
-> 0x10604f980: popq %rax
0x10604f981: cmpq (%rax), %rax
// $rsi = 0
0x10604f984: xorl %esi, %esi
// $rcx = $rdx = $expressionBottom
0x10604f986: movq -0x48(%rbp), %rcx
0x10604f98a: leaq -0x48(%rbp), %rdx
0x10604f98e: jmp 0x10604f9a6
// BasicLockObject's loop
0x10604f990: cmpq $0x0, 0x8(%rcx)
0x10604f998: cmoveq %rcx, %rsi
0x10604f99c: cmpq 0x8(%rcx), %rax
0x10604f9a0: je 0x10604f9ab
0x10604f9a2: addq $0x10, %rcx
0x10604f9a6: cmpq %rdx, %rcx
0x10604f9a9: jne 0x10604f990
// if BasicLockObject choosed, goto doLockBiz
0x10604f9ab: testq %rsi, %rsi
0x10604f9ae: jne 0x10604f9dc
// ExpressionStack's move
0x10604f9b4: movq -0x48(%rbp), %rsi
0x10604f9b8: subq $0x10, %rsp
0x10604f9bc: subq $0x10, %rsi
0x10604f9c0: movq %rsp, %rcx
0x10604f9c3: movq %rsi, -0x48(%rbp)
0x10604f9c7: jmp 0x10604f9d7
0x10604f9cc: movq 0x10(%rcx), %rdx
0x10604f9d0: movq %rdx, (%rcx)
0x10604f9d3: addq $0x8, %rcx
0x10604f9d7: cmpq %rsi, %rcx
0x10604f9da: jne 0x10604f9cc
// after this
// (lldb) x 0x000070000c648620 -c 0x100
// 0x70000c648620: 88 8b bb 47 07 00 00 00 88 8b bb 47 07 00 00 00 ..?G......?G....
// 0x70000c648630: 20 86 64 0c 00 70 00 00 24 10 89 1c 01 00 00 00 .d..p..$.......
// 0x70000c648640: a8 86 64 0c 00 70 00 00 08 11 89 1c 01 00 00 00 ?.d..p..........
// 0x70000c648630 : 0x70000c648620 : expression bottom
// 0x70000c648620 : allocated BasicLockObject
// doLockBiz
0x10604f9dc: incq %r13
// update BasicLockObject.obj = $lockObj
0x10604f9df: movq %rax, 0x8(%rsi)
这部分的代码处理, 主要是需要在 $expresstionBottom 和 表达式栈 之间寻找可用的 BasicObjectLock(obj == null 表示可用)
如果没有, 则新建一个 BasicObjectLock 放在 $expresstionBottom 和 表达式栈 之间
在 lock_object 之前有一步处理, 更新 BasicObjectLock.obj = $lockObj
以上的这部分汇编代码 逻辑上可以参考 上面的代码逻辑, 也可以根据这里的实际情况 走一遍
也可以 扩展一下 模拟存在 多个 BasicObjectLock 的场景, 尝试一下
这里执行完之后, $rsi 指向一个 BasicObjectLock, BasicObjectLock.obj 为需要加锁的对象
曾经在这篇文章中看到 R大 说过
如果要实现obj1所指向的对象在method返回是理解释放,一种办法是实现Java对象的“栈上分配”(stack allocation)。简单来说就是把对象自身也分配在栈上,那么栈帧释放的时候那个对象也自然就释放掉了。栈上分配需要做“逃逸分析”(escape ****ysis)来判断到底某个对象是否有引用“逃逸”到某个方法之外,如果没有,就意味着该对象与该方法调用的生命周期一致,就可以将对象分配在栈上。
但这样做非常复杂而且实际好处不够大,所以现在主流JVM也无一实现对象的栈上分配。所以这里就不详细说了,在现实生产中您是不会见到这种做法的。
嘿嘿, 但是这里的 BasicObjectLock 应该算是一种狭义上的 栈上分配吧 ..
lock_object 的业务
interp_ma**_x86.cpp lock_object
// Lock object
//
// Args:
// rdx, c_rarg1: BasicObjectLock to be used for locking
//
// Kills:
// rax, rbx
void InterpreterMacroAssembler::lock_object(Register lock_reg) {
assert(lock_reg == LP64_ONLY(c_rarg1) NOT_LP64(rdx),
"The argument is only for looks. It must be c_rarg1");
if (UseHeavyMonitors) {
call_VM(noreg,
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
lock_reg);
} else {
Label done;
const Register swap_reg = rax; // Must use rax for cmpxchg instruction
const Register tmp_reg = rbx; // Will be passed to biased_locking_enter to avoid a
// problematic case where tmp_reg = no_reg.
const Register obj_reg = LP64_ONLY(c_rarg3) NOT_LP64(rcx); // Will contain the oop
const int obj_offset = BasicObjectLock::obj_offset_in_bytes();
const int lock_offset = BasicObjectLock::lock_offset_in_bytes ();
const int mark_offset = lock_offset +
BasicLock::displaced_header_offset_in_bytes();
Label slow_case;
// Load object pointer into obj_reg
movptr(obj_reg, Address(lock_reg, obj_offset));
if (UseBiasedLocking) {
biased_locking_enter(lock_reg, obj_reg, swap_reg, tmp_reg, false, done, &slow_case);
}
// Load immediate 1 into swap_reg %rax
movl(swap_reg, (int32_t)1);
// Load (object->mark() | 1) into swap_reg %rax
orptr(swap_reg, Address(obj_reg, 0));
// Save (object->mark() | 1) into BasicLock's displaced header
movptr(Address(lock_reg, mark_offset), swap_reg);
assert(lock_offset == 0,
"displaced header must be first word in BasicObjectLock");
if (os::is_MP()) lock();
cmpxchgptr(lock_reg, Address(obj_reg, 0));
if (PrintBiasedLockingStatistics) {
cond_inc32(Assembler::zero,
ExternalAddress((address) BiasedLocking::fast_path_entry_count_addr()));
}
jcc(Assembler::zero, done);
const int zero_bits = LP64_ONLY(7) NOT_LP64(3);
// Test if the oopMark is an obvious stack pointer, i.e.,
// 1) (mark & zero_bits) == 0, and
// 2) rsp <= mark < mark + os::pagesize()
//
// These 3 tests can be done by evaluating the following
// expression: ((mark - rsp) & (zero_bits - os::vm_page_size())),
// assuming both stack pointer and pagesize have their
// least significant bits clear.
// NOTE: the oopMark is in swap_reg %rax as the result of cmpxchg
subptr(swap_reg, rsp);
andptr(swap_reg, zero_bits - os::vm_page_size());
// Save the test result, for recursive case, the result is zero
movptr(Address(lock_reg, mark_offset), swap_reg);
if (PrintBiasedLockingStatistics) {
cond_inc32(Assembler::zero,
ExternalAddress((address) BiasedLocking::fast_path_entry_count_addr()));
}
jcc(Assembler::zero, done);
bind(slow_case);
// Call the runtime routine for slow case
call_VM(noreg,
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
lock_reg);
bind(done);
}
}
macroAssembler_x86.cpp biased_locking_enter
int MacroAssembler::biased_locking_enter(Register lock_reg,
Register obj_reg,
Register swap_reg,
Register tmp_reg,
bool swap_reg_contains_mark,
Label& done,
Label* slow_case,
BiasedLockingCounters* counters) {
assert(UseBiasedLocking, "why call this otherwise?");
assert(swap_reg == rax, "swap_reg must be rax for cmpxchgq");
assert(tmp_reg != noreg, "tmp_reg must be supplied");
assert_different_registers(lock_reg, obj_reg, swap_reg, tmp_reg);
assert(markOopDesc::age_shift == markOopDesc::lock_bits + markOopDesc::biased_lock_bits, "biased locking makes assumptions about bit layout");
Address mark_addr (obj_reg, oopDesc::mark_offset_in_bytes());
NOT_LP64( Address saved_mark_addr(lock_reg, 0); )
if (PrintBiasedLockingStatistics && counters == NULL) {
counters = BiasedLocking::counters();
}
// Biased locking
// See whether the lock is currently biased toward our thread and
// whether the epoch is still valid
// Note that the runtime guarantees sufficient alignment of JavaThread
// pointers to allow age to be placed into low bits
// First check to see whether biasing is even enabled for this object
Label cas_label;
int null_check_offset = -1;
if (!swap_reg_contains_mark) {
null_check_offset = offset();
movptr(swap_reg, mark_addr);
}
movptr(tmp_reg, swap_reg);
andptr(tmp_reg, markOopDesc::biased_lock_mask_in_place);
cmpptr(tmp_reg, markOopDesc::biased_lock_pattern);
jcc(Assembler::notEqual, cas_label);
// The bias pattern is present in the object's header. Need to check
// whether the bias owner and the epoch are both still current.
#ifndef _LP64
// Note that because there is no current thread register on x86_32 we
// need to store off the mark word we read out of the object to
// avoid reloading it and needing to recheck invariants below. This
// store is unfortunate but it makes the overall code shorter and
// simpler.
movptr(saved_mark_addr, swap_reg);
#endif
if (swap_reg_contains_mark) {
null_check_offset = offset();
}
load_prototype_header(tmp_reg, obj_reg);
#ifdef _LP64
orptr(tmp_reg, r15_thread);
xorptr(tmp_reg, swap_reg);
Register header_reg = tmp_reg;
#else
xorptr(tmp_reg, swap_reg);
get_thread(swap_reg);
xorptr(swap_reg, tmp_reg);
Register header_reg = swap_reg;
#endif
andptr(header_reg, ~((int) markOopDesc::age_mask_in_place));
if (counters != NULL) {
cond_inc32(Assembler::zero,
ExternalAddress((address) counters->biased_lock_entry_count_addr()));
}
jcc(Assembler::equal, done);
Label try_revoke_bias;
Label try_rebias;
// At this point we know that the header has the bias pattern and
// that we are not the bias owner in the current epoch. We need to
// figure out more details about the state of the header in order to
// know what operations can be legally performed on the object's
// header.
// If the low three bits in the xor result aren't clear, that means
// the prototype header is no longer biased and we have to revoke
// the bias on this object.
testptr(header_reg, markOopDesc::biased_lock_mask_in_place);
jccb(Assembler::notZero, try_revoke_bias);
// Biasing is still enabled for this data type. See whether the
// epoch of the current bias is still valid, meaning that the epoch
// bits of the mark word are equal to the epoch bits of the
// prototype header. (Note that the prototype header's epoch bits
// only change at a safepoint.) If not, attempt to rebias the object
// toward the current thread. Note that we must be absolutely sure
// that the current epoch is invalid in order to do this because
// otherwise the manipulations it performs on the mark word are
// illegal.
testptr(header_reg, markOopDesc::epoch_mask_in_place);
jccb(Assembler::notZero, try_rebias);
// The epoch of the current bias is still valid but we know nothing
// about the owner; it might be set or it might be clear. Try to
// acquire the bias of the object using an atomic operation. If this
// fails we will go in to the runtime to revoke the object's bias.
// Note that we first construct the presumed unbiased header so we
// don't accidentally blow away another thread's valid bias.
NOT_LP64( movptr(swap_reg, saved_mark_addr); )
andptr(swap_reg,
markOopDesc::biased_lock_mask_in_place | markOopDesc::age_mask_in_place | markOopDesc::epoch_mask_in_place);
#ifdef _LP64
movptr(tmp_reg, swap_reg);
orptr(tmp_reg, r15_thread);
#else
get_thread(tmp_reg);
orptr(tmp_reg, swap_reg);
#endif
if (os::is_MP()) {
lock();
}
cmpxchgptr(tmp_reg, mark_addr); // compare tmp_reg and swap_reg
// If the biasing toward our thread failed, this means that
// another thread succeeded in biasing it toward itself and we
// need to revoke that bias. The revocation will occur in the
// interpreter runtime in the slow case.
if (counters != NULL) {
cond_inc32(Assembler::zero,
ExternalAddress((address) counters->anonymously_biased_lock_entry_count_addr()));
}
if (slow_case != NULL) {
jcc(Assembler::notZero, *slow_case);
}
jmp(done);
bind(try_rebias);
// At this point we know the epoch has expired, meaning that the
// current "bias owner", if any, is actually invalid. Under these
// circumstances _only_, we are allowed to use the current header's
// value as the comparison value when doing the cas to acquire the
// bias in the current epoch. In other words, we allow transfer of
// the bias from one thread to another directly in this situation.
//
// FIXME: due to a lack of registers we currently blow away the age
// bits in this situation. Should attempt to preserve them.
load_prototype_header(tmp_reg, obj_reg);
#ifdef _LP64
orptr(tmp_reg, r15_thread);
#else
get_thread(swap_reg);
orptr(tmp_reg, swap_reg);
movptr(swap_reg, saved_mark_addr);
#endif
if (os::is_MP()) {
lock();
}
cmpxchgptr(tmp_reg, mark_addr); // compare tmp_reg and swap_reg
// If the biasing toward our thread failed, then another thread
// succeeded in biasing it toward itself and we need to revoke that
// bias. The revocation will occur in the runtime in the slow case.
if (counters != NULL) {
cond_inc32(Assembler::zero,
ExternalAddress((address) counters->rebiased_lock_entry_count_addr()));
}
if (slow_case != NULL) {
jcc(Assembler::notZero, *slow_case);
}
jmp(done);
bind(try_revoke_bias);
// The prototype mark in the klass doesn't have the bias bit set any
// more, indicating that objects of this data type are not supposed
// to be biased any more. We are going to try to reset the mark of
// this object to the prototype value and fall through to the
// CAS-based locking scheme. Note that if our CAS fails, it means
// that another thread raced us for the privilege of revoking the
// bias of this particular object, so it's okay to continue in the
// normal locking code.
//
// FIXME: due to a lack of registers we currently blow away the age
// bits in this situation. Should attempt to preserve them.
NOT_LP64( movptr(swap_reg, saved_mark_addr); )
load_prototype_header(tmp_reg, obj_reg);
if (os::is_MP()) {
lock();
}
cmpxchgptr(tmp_reg, mark_addr); // compare tmp_reg and swap_reg
// Fall through to the normal CAS-based lock, because no matter what
// the result of the above CAS, some thread must have succeeded in
// removing the bias bit from the object's header.
if (counters != NULL) {
cond_inc32(Assembler::zero,
ExternalAddress((address) counters->revoked_lock_entry_count_addr()));
}
bind(cas_label);
return null_check_offset;
}
lock_object 这部分生成的汇编如下
(lldb) dis -s 0x000000011652e151 -c 300
0x11652e151: movq 0x8(%rsi), %rcx
// biased_locking_enter
// tmp_reg = rbx, swap_reg = rax, obj_reg = rcx
// if(!obj.mark().has_bias_pattern()) goto biased_locking_enter_end;
0x11652e155: movq (%rcx), %rax
0x11652e158: movq %rax, %rbx
0x11652e15b: andq $0x7, %rbx
0x11652e15f: cmpq $0x5, %rbx
0x11652e163: jne 0x11652e226
// $rbx = obj->klass->prototype_header
// $rbx = ¤tThread | $rbx
0x11652e169: movl 0x8(%rcx), %ebx
0x11652e16c: shlq $0x3, %rbx
0x11652e170: movq 0xb0(%rbx), %rbx
0x11652e177: orq %r15, %rbx
// $rax = obj.mark()
0x11652e17a: xorq %rax, %rbx
// mask 掉 $rbx 的 age 的几个 bit
// 除了 age_bits 其他都相同(重入) goto done;
// counters != NULL && counters->biased_lock_entry_count_addr ++
0x11652e17d: andq $-0x79, %rbx
0x11652e181: jne 0x11652e190
0x11652e187: pushfq
0x11652e188: lock
0x11652e189: incl -0xb2934ee(%rip) ; BiasedLocking::_counters + 5
0x11652e18f: popfq
0x11652e190: je 0x11652e4cd
// if (lockBits != 0) goto try_revoke_bias
0x11652e196: testq $0x7, %rbx
0x11652e19d: jne 0x11652e204
// if (epoch != 0) goto try_rebias
0x11652e19f: testq $0x300, %rbx ; imm = 0x300
0x11652e1a6: jne 0x11652e1d4
// epoch is valid
// 这里使用的是 $eax(obj.mark), 首先是 mask 掉了 rax 的线程的数据, 然后再 写入 $currentThread
0x11652e1a8: andq $0x37f, %rax ; imm = 0x37F
0x11652e1af: movq %rax, %rbx
0x11652e1b2: orq %r15, %rbx
0x11652e1b5: lock
0x11652e1b6: cmpxchgq %rbx, (%rcx)
// counters != NULL && counters->anonymously_biased_lock_entry_count_addr ++
0x11652e1ba: jne 0x11652e1c9
0x11652e1c0: pushfq
0x11652e1c1: lock
0x11652e1c2: incl -0xb293523(%rip) ; BiasedLocking::_counters + 9
0x11652e1c8: popfq
0x11652e1c9: jne 0x11652e26d
0x11652e1cf: jmp 0x11652e4cd
// try_rebias, age 丢了 ?
0x11652e1d4: movl 0x8(%rcx), %ebx
0x11652e1d7: shlq $0x3, %rbx
0x11652e1db: movq 0xb0(%rbx), %rbx
0x11652e1e2: orq %r15, %rbx
0x11652e1e5: lock
0x11652e1e6: cmpxchgq %rbx, (%rcx)
// counters != NULL && counters->rebiased_lock_entry_count_addr ++
0x11652e1ea: jne 0x11652e1f9
0x11652e1f0: pushfq
0x11652e1f1: lock
0x11652e1f2: incl -0xb29354f(%rip) ; BiasedLocking::_counters + 13
0x11652e1f8: popfq
0x11652e1f9: jne 0x11652e26d
0x11652e1ff: jmp 0x11652e4cd
// try_revoke_bias
// obj.set_mark_oop(obj->klass->prototype_header)
0x11652e204: movl 0x8(%rcx), %ebx
0x11652e207: shlq $0x3, %rbx
0x11652e20b: movq 0xb0(%rbx), %rbx
0x11652e212: lock
0x11652e213: cmpxchgq %rbx, (%rcx)
// counters != NULL && counters->revoked_lock_entry_count_addr ++
0x11652e217: jne 0x11652e226
0x11652e21d: pushfq
0x11652e21e: lock
0x11652e21f: incl -0xb293578(%rip) ; BiasedLocking::_counters + 17
0x11652e225: popfq
// cmpxchgptr(lock_reg, Address(obj_reg, 0));
// 加轻量级锁
0x11652e226: movl $0x1, %eax
0x11652e22b: orq (%rcx), %rax
0x11652e22e: movq %rax, (%rsi)
0x11652e231: lock
0x11652e232: cmpxchgq %rsi, (%rcx)
0x11652e236: jne 0x11652e245
0x11652e23c: pushfq
0x11652e23d: lock
0x11652e23e: incl -0xb293593(%rip) ; BiasedLocking::_counters + 21
0x11652e244: popfq
0x11652e245: je 0x11652e4cd
// 轻量级锁重入
0x11652e24b: subq %rsp, %rax
0x11652e24e: andq $-0xff9, %rax ; imm = 0xF007
0x11652e255: movq %rax, (%rsi)
0x11652e258: jne 0x11652e267
0x11652e25e: pushfq
0x11652e25f: lock
0x11652e260: incl -0xb2935b5(%rip) ; BiasedLocking::_counters + 21
0x11652e266: popfq
0x11652e267: je 0x11652e4cd
// InterpreterRuntime::monitorenter
0x11652e26d: callq 0x11652e277
0x11652e272: jmp 0x11652e4cd
0x11652e277: leaq 0x8(%rsp), %rax
0x11652e27c: movq %r13, -0x40(%rbp)
0x11652e280: cmpq $0x0, -0x10(%rbp)
0x11652e288: je 0x11652e305
0x11652e28e: movq %rsp, -0x28(%rsp)
0x11652e293: subq $0x80, %rsp
0x11652e29a: movq %rax, 0x78(%rsp)
0x11652e29f: movq %rcx, 0x70(%rsp)
0x11652e2a4: movq %rdx, 0x68(%rsp)
0x11652e2a9: movq %rbx, 0x60(%rsp)
0x11652e2ae: movq %rbp, 0x50(%rsp)
0x11652e2b3: movq %rsi, 0x48(%rsp)
0x11652e2b8: movq %rdi, 0x40(%rsp)
0x11652e2bd: movq %r8, 0x38(%rsp)
0x11652e2c2: movq %r9, 0x30(%rsp)
0x11652e2c7: movq %r10, 0x28(%rsp)
0x11652e2cc: movq %r11, 0x20(%rsp)
0x11652e2d1: movq %r12, 0x18(%rsp)
0x11652e2d6: movq %r13, 0x10(%rsp)
0x11652e2db: movq %r14, 0x8(%rsp)
0x11652e2e0: movq %r15, (%rsp)
0x11652e2e4: movabsq $0x10b0984ed, %rdi ; imm = 0x10B0984ED
0x11652e2ee: movabsq $0x11652e28e, %rsi ; imm = 0x11652E28E
0x11652e2f8: movq %rsp, %rdx
0x11652e2fb: andq $-0x10, %rsp
0x11652e2ff: callq 0x10ab6af60 ; MacroAssembler::debug64 at macroAssembler_x86.cpp:862
0x11652e304: hlt
0x11652e305: pushq %r10
0x11652e307: cmpq -0xb217d1e(%rip), %r12 ; Universe::_narrow_ptrs_base
0x11652e30e: je 0x11652e38b
0x11652e314: movq %rsp, -0x28(%rsp)
0x11652e319: subq $0x80, %rsp
0x11652e320: movq %rax, 0x78(%rsp)
0x11652e325: movq %rcx, 0x70(%rsp)
0x11652e32a: movq %rdx, 0x68(%rsp)
0x11652e32f: movq %rbx, 0x60(%rsp)
0x11652e334: movq %rbp, 0x50(%rsp)
0x11652e339: movq %rsi, 0x48(%rsp)
0x11652e33e: movq %rdi, 0x40(%rsp)
0x11652e343: movq %r8, 0x38(%rsp)
0x11652e348: movq %r9, 0x30(%rsp)
0x11652e34d: movq %r10, 0x28(%rsp)
0x11652e352: movq %r11, 0x20(%rsp)
0x11652e357: movq %r12, 0x18(%rsp)
0x11652e35c: movq %r13, 0x10(%rsp)
0x11652e361: movq %r14, 0x8(%rsp)
0x11652e366: movq %r15, (%rsp)
0x11652e36a: movabsq $0x10b0d4efc, %rdi ; imm = 0x10B0D4EFC
0x11652e374: movabsq $0x11652e314, %rsi ; imm = 0x11652E314
0x11652e37e: movq %rsp, %rdx
0x11652e381: andq $-0x10, %rsp
0x11652e385: callq 0x10ab6af60 ; MacroAssembler::debug64 at macroAssembler_x86.cpp:862
0x11652e38a: hlt
0x11652e38b: popq %r10
0x11652e38d: movq %r15, %rdi
0x11652e390: movq %rbp, 0x218(%r15)
0x11652e397: movq %rax, 0x208(%r15)
0x11652e39e: testl $0xf, %esp
0x11652e3a4: je 0x11652e3bc
0x11652e3aa: subq $0x8, %rsp
0x11652e3ae: callq 0x10a8d2360 ; InterpreterRuntime::monitorenter at interpreterRuntime.cpp:635
0x11652e3b3: addq $0x8, %rsp
0x11652e3b7: jmp 0x11652e3c1
0x11652e3bc: callq 0x10a8d2360 ; InterpreterRuntime::monitorenter at interpreterRuntime.cpp:635
0x11652e3c1: pushq %rax
0x11652e3c2: pushq %rdi
0x11652e3c3: pushq %rsi
0x11652e3c4: pushq %rdx
0x11652e3c5: pushq %rcx
0x11652e3c6: pushq %r8
0x11652e3c8: pushq %r9
0x11652e3ca: pushq %r10
0x11652e3cc: pushq %r11
0x11652e3ce: testl $0xf, %esp
0x11652e3d4: je 0x11652e3ec
0x11652e3da: subq $0x8, %rsp
0x11652e3de: callq 0x10a003ae0 ; Thread::current at thread.hpp:660
0x11652e3e3: addq $0x8, %rsp
0x11652e3e7: jmp 0x11652e3f1
0x11652e3ec: callq 0x10a003ae0 ; Thread::current at thread.hpp:660
0x11652e3f1: popq %r11
0x11652e3f3: popq %r10
0x11652e3f5: popq %r9
0x11652e3f7: popq %r8
0x11652e3f9: popq %rcx
0x11652e3fa: popq %rdx
0x11652e3fb: popq %rsi
0x11652e3fc: popq %rdi
0x11652e3fd: cmpq %rax, %r15
0x11652e400: je 0x11652e47d
0x11652e406: movq %rsp, -0x28(%rsp)
0x11652e40b: subq $0x80, %rsp
0x11652e412: movq %rax, 0x78(%rsp)
0x11652e417: movq %rcx, 0x70(%rsp)
0x11652e41c: movq %rdx, 0x68(%rsp)
0x11652e421: movq %rbx, 0x60(%rsp)
0x11652e426: movq %rbp, 0x50(%rsp)
0x11652e42b: movq %rsi, 0x48(%rsp)
0x11652e430: movq %rdi, 0x40(%rsp)
0x11652e435: movq %r8, 0x38(%rsp)
0x11652e43a: movq %r9, 0x30(%rsp)
0x11652e43f: movq %r10, 0x28(%rsp)
0x11652e444: movq %r11, 0x20(%rsp)
0x11652e449: movq %r12, 0x18(%rsp)
0x11652e44e: movq %r13, 0x10(%rsp)
0x11652e453: movq %r14, 0x8(%rsp)
0x11652e458: movq %r15, (%rsp)
0x11652e45c: movabsq $0x10b0d5043, %rdi ; imm = 0x10B0D5043
0x11652e466: movabsq $0x11652e406, %rsi ; imm = 0x11652E406
0x11652e470: movq %rsp, %rdx
0x11652e473: andq $-0x10, %rsp
0x11652e477: callq 0x10ab6af60 ; MacroAssembler::debug64 at macroAssembler_x86.cpp:862
0x11652e47c: hlt
0x11652e47d: popq %rax
0x11652e47e: movabsq $0x0, %r10
0x11652e488: movq %r10, 0x208(%r15)
0x11652e48f: movabsq $0x0, %r10
0x11652e499: movq %r10, 0x218(%r15)
0x11652e4a0: movabsq $0x0, %r10
0x11652e4aa: movq %r10, 0x210(%r15)
0x11652e4b1: cmpq $0x0, 0x8(%r15)
0x11652e4b9: je 0x11652e4c4
0x11652e4bf: jmp 0x1165037a0
0x11652e4c4: movq -0x40(%rbp), %r13
0x11652e4c8: movq -0x38(%rbp), %r14
0x11652e4cc: retq
0x11652e4cd: int3
通过以上的汇编代码块, 可以整理出一些规则
1. 如果 (!obj.mark().has_bias_pattern()) 对象不支持偏向锁(未启用偏向锁, 类不支持, 或者创建于更新 instanceKlass.prototype_header之前) 则走获取 轻量级锁 的流程
2. 如果 (((obj->klass->prototype_header | ¤tThread) ^ (0b101)) & -0x79) 为 0(重入), 直接 done
3. 如果 "(lockBits != 0)", 走 取消偏向锁, 取消成功 或者 失败, 走获取 轻量级锁的流程
4. 如果 "(epoch != 0)" 重新获取偏向锁, 获取成功 done, 获取失败 走 InterpreterRuntime::monitorenter
5. 走 获取偏向锁的流程, cas 更新对象头, 更新成功 done, 更新失败 走 InterpreterRuntime::monitorenter
6. 如果 没有启用偏向锁 并且 没有只用重量级锁, 或者以上的走 轻量级锁 的部分流程, 尝试获取 轻量级锁, 获取成功, 直接 done, 获取失败 判断是否是 轻量级锁 重入, 如果是 直接 done, 否则 走 InterpreterRuntime::monitorenter
7. 走 InterpreterRuntime::monitorenter
像我们这里就比较简单, 直接走的是 5, 更新了 对象头, 并且更新成功
更新了之后, 输出一下 lockObj 的状态信息, 偏向锁的相关信息就更新到 对象头了
(lldb) p ((oopDesc*)0x0000000747bb8d18)->print()
com.hx.test04.Test26SynchronizeObject
{0x0000000747bb8d18} - 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"{0x0000000747bb8d40} (e8f771a8 0)
(lldb) p ((oopDesc*)0x0000000747bb8d18)->mark()->has_bias_pattern()
(bool) $1 = true
(lldb) p ((oopDesc*)0x0000000747bb8d18)->mark()->biased_locker()->threadObj()->print()
java.lang.Thread
{0x0000000747f069d8} - klass: 'java/lang/Thread'
- ---- fields (total size 47 words):
- private 'priority' 'I' @12 5
- private 'eetop' 'J' @16 4303366144 (802800 1)
- private 'stackSize' 'J' @24 0 (0 0)
- private 'nativeParkEventPointer' 'J' @32 0 (0 0)
- private 'tid' 'J' @40 1 (1 0)
- private volatile 'threadStatus' 'I' @48 5
- private 'single_step' 'Z' @52 false
- private 'daemon' 'Z' @53 false
- private 'stillborn' 'Z' @54 false
- private volatile 'name' 'Ljava/lang/String;' @56 "main"{0x0000000747f06b50} (e8fe0d6a 0)
- private 'threadQ' 'Ljava/lang/Thread;' @60 NULL (0 0)
- private 'target' 'Ljava/lang/Runnable;' @64 NULL (0 e8fe0c85)
- private 'group' 'Ljava/lang/ThreadGroup;' @68 a 'java/lang/ThreadGroup'{0x0000000747f06428} (e8fe0c85 e8fc0af7)
- private 'contextClassLoader' 'Ljava/lang/ClassLoader;' @72 a 'jdk/internal/loader/ClassLoaders$AppClassLoader'{0x0000000747e057b8} (e8fc0af7 e8fe0d91)
- private 'inheritedAccessControlContext' 'Ljava/security/AccessControlContext;' @76 a 'java/security/AccessControlContext'{0x0000000747f06c88} (e8fe0d91 e8fe2fb8)
- 'threadLocals' 'Ljava/lang/ThreadLocal$ThreadLocalMap;' @80 a 'java/lang/ThreadLocal$ThreadLocalMap'{0x0000000747f17dc0} (e8fe2fb8 0)
- 'inheritableThreadLocals' 'Ljava/lang/ThreadLocal$ThreadLocalMap;' @84 NULL (0 0)
- volatile 'parkBlocker' 'Ljava/lang/Object;' @88 NULL (0 0)
- private volatile 'blocker' 'Lsun/nio/ch/Interruptible;' @92 NULL (0 e8fe0d70)
- private final 'blockerLock' 'Ljava/lang/Object;' @96 a 'java/lang/Object'{0x0000000747f06b80} (e8fe0d70 0)
- private volatile 'uncaughtExceptionHandler' 'Ljava/lang/Thread$UncaughtExceptionHandler;' @100 NULL (0 0)
- 'threadLocalRandomSeed' 'J' @232 0 (0 0)
- 'threadLocalRandomProbe' 'I' @240 0
- 'threadLocalRandomSecondarySeed' 'I' @244 0
添加偏向锁是否丢失了age?
Process 859 stopped
* thread #5, stop reason = breakpoint 3.1
frame #0: 0x000000010504f980
-> 0x10504f980: popq %rax
0x10504f981: cmpq (%rax), %rax
0x10504f984: xorl %esi, %esi
0x10504f986: movq -0x48(%rbp), %rcx
Target 0: (java) stopped.
(lldb) re r
General Purpose Registers:
rax = 0x0000000747bb8b88
rbx = 0x00000000000000c2
rcx = 0x0000000000000008
rdx = 0x0000000747bb8ba8
rdi = 0x0000000100805800
rsi = 0x0000700007cfc5c8
rbp = 0x0000700007cfc678
rsp = 0x0000700007cfc628
r8 = 0x0000000000000000
r9 = 0x0000000000000020
r10 = 0x0000000103b0b270 libjvm.dylib`TemplateInterpreter::_active_table + 18432
r11 = 0x00006fff074f5400
r12 = 0x0000000000000000
r13 = 0x000000011c87302b
r14 = 0x0000700007cfc6a8
r15 = 0x0000000100805800
rip = 0x000000010504f980
rflags = 0x0000000000000206
cs = 0x000000000000002b
fs = 0x0000000000000000
gs = 0x0000000000000000
(lldb) p ((oopDesc*)0x0000000747bb8b88)->print()
com.hx.test04.Test26SynchronizeObject
{0x0000000747bb8b88} - 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"{0x0000000747bb8bb0} (e8f77176 0)
(lldb) p ((oopDesc*)0x0000000747bb8b88)->age()
(uint) $1 = 0
(lldb) p ((oopDesc*)0x0000000747bb8b88)->incr_age()
(lldb) p ((oopDesc*)0x0000000747bb8b88)->age()
(uint) $2 = 1
(lldb) b 0x10504f9e3
Breakpoint 4: address = 0x000000010504f9e3
(lldb) c
Process 859 resuming
Process 859 stopped
* thread #5, stop reason = breakpoint 4.1
frame #0: 0x000000010504f9e3
-> 0x10504f9e3: movq 0x8(%rsi), %rcx
0x10504f9e7: movq (%rcx), %rax
0x10504f9ea: movq %rax, %rbx
0x10504f9ed: andq $0x7, %rbx
Target 0: (java) stopped.
(lldb) stepi -c 4
Process 859 stopped
* thread #5, stop reason = instruction step into
frame #0: 0x000000010504f9f1
-> 0x10504f9f1: cmpq $0x5, %rbx
0x10504f9f5: jne 0x10504fa7c
0x10504f9fb: movl 0x8(%rcx), %ebx
0x10504f9fe: shlq $0x3, %rbx
Target 0: (java) stopped.
(lldb) stepi -c 2
Process 859 stopped
* thread #5, stop reason = instruction step into
frame #0: 0x000000010504f9fb
-> 0x10504f9fb: movl 0x8(%rcx), %ebx
0x10504f9fe: shlq $0x3, %rbx
0x10504fa02: movq 0xb0(%rbx), %rbx
0x10504fa09: orq %r15, %rbx
Target 0: (java) stopped.
(lldb) stepi -c 4
Process 859 stopped
* thread #5, stop reason = instruction step into
frame #0: 0x000000010504fa0c
-> 0x10504fa0c: xorq %rax, %rbx
0x10504fa0f: andq $-0x79, %rbx
0x10504fa13: je 0x10504fd05
0x10504fa19: testq $0x7, %rbx
Target 0: (java) stopped.
(lldb) stepi -c 2
Process 859 stopped
* thread #5, stop reason = instruction step into
frame #0: 0x000000010504fa13
-> 0x10504fa13: je 0x10504fd05
0x10504fa19: testq $0x7, %rbx
0x10504fa20: jne 0x10504fa69
0x10504fa22: testq $0x300, %rbx ; imm = 0x300
Target 0: (java) stopped.
(lldb) stepi -c 2
Process 859 stopped
* thread #5, stop reason = instruction step into
frame #0: 0x000000010504fa20
-> 0x10504fa20: jne 0x10504fa69
0x10504fa22: testq $0x300, %rbx ; imm = 0x300
0x10504fa29: jne 0x10504fa48
0x10504fa2b: andq $0x37f, %rax ; imm = 0x37F
Target 0: (java) stopped.
(lldb) stepi -c 2
Process 859 stopped
* thread #5, stop reason = instruction step into
frame #0: 0x000000010504fa29
-> 0x10504fa29: jne 0x10504fa48
0x10504fa2b: andq $0x37f, %rax ; imm = 0x37F
0x10504fa32: movq %rax, %rbx
0x10504fa35: orq %r15, %rbx
Target 0: (java) stopped.
(lldb) stepi
Process 859 stopped
* thread #5, stop reason = instruction step into
frame #0: 0x000000010504fa2b
-> 0x10504fa2b: andq $0x37f, %rax ; imm = 0x37F
0x10504fa32: movq %rax, %rbx
0x10504fa35: orq %r15, %rbx
0x10504fa38: lock
Target 0: (java) stopped.
(lldb) stepi -c 5
Process 859 stopped
* thread #5, stop reason = instruction step into
frame #0: 0x000000010504fa43
-> 0x10504fa43: jmp 0x10504fd05
0x10504fa48: movl 0x8(%rcx), %ebx
0x10504fa4b: shlq $0x3, %rbx
0x10504fa4f: movq 0xb0(%rbx), %rbx
Target 0: (java) stopped.
(lldb) p ((oopDesc*)0x0000000747bb8b88)->age()
(uint) $3 = 1
(lldb) p ((oopDesc*)0x0000000747bb8b88)->mark()->biased_locker()->threadObj()->print()
java.lang.Thread
{0x0000000747f069d8} - klass: 'java/lang/Thread'
- ---- fields (total size 47 words):
- private 'priority' 'I' @12 5
- private 'eetop' 'J' @16 4303378432 (805800 1)
- private 'stackSize' 'J' @24 0 (0 0)
- private 'nativeParkEventPointer' 'J' @32 0 (0 0)
- private 'tid' 'J' @40 1 (1 0)
- private volatile 'threadStatus' 'I' @48 5
- private 'single_step' 'Z' @52 false
- private 'daemon' 'Z' @53 false
- private 'stillborn' 'Z' @54 false
- private volatile 'name' 'Ljava/lang/String;' @56 "main"{0x0000000747f06b50} (e8fe0d6a 0)
- private 'threadQ' 'Ljava/lang/Thread;' @60 NULL (0 0)
- private 'target' 'Ljava/lang/Runnable;' @64 NULL (0 e8fe0c85)
- private 'group' 'Ljava/lang/ThreadGroup;' @68 a 'java/lang/ThreadGroup'{0x0000000747f06428} (e8fe0c85 e8fc0af7)
- private 'contextClassLoader' 'Ljava/lang/ClassLoader;' @72 a 'jdk/internal/loader/ClassLoaders$AppClassLoader'{0x0000000747e057b8} (e8fc0af7 e8fe0d91)
- private 'inheritedAccessControlContext' 'Ljava/security/AccessControlContext;' @76 a 'java/security/AccessControlContext'{0x0000000747f06c88} (e8fe0d91 e8fe2fb8)
- 'threadLocals' 'Ljava/lang/ThreadLocal$ThreadLocalMap;' @80 a 'java/lang/ThreadLocal$ThreadLocalMap'{0x0000000747f17dc0} (e8fe2fb8 0)
- 'inheritableThreadLocals' 'Ljava/lang/ThreadLocal$ThreadLocalMap;' @84 NULL (0 0)
- volatile 'parkBlocker' 'Ljava/lang/Object;' @88 NULL (0 0)
- private volatile 'blocker' 'Lsun/nio/ch/Interruptible;' @92 NULL (0 e8fe0d70)
- private final 'blockerLock' 'Ljava/lang/Object;' @96 a 'java/lang/Object'{0x0000000747f06b80} (e8fe0d70 0)
- private volatile 'uncaughtExceptionHandler' 'Ljava/lang/Thread$UncaughtExceptionHandler;' @100 NULL (0 0)
- 'threadLocalRandomSeed' 'J' @232 0 (0 0)
- 'threadLocalRandomProbe' 'I' @240 0
- 'threadLocalRandomSecondarySeed' 'I' @244 0
首先是结果 没有, 当时我看 添加偏向锁这段, 我看错了, 看成了 通过 ebx 来 mask 除了线程其他的位, 再设置偏向线程
但是实际 仔细一看 是使用的 eax, eax 对应的是 obj 原有的 mark 的信息, 因此 age 信息没有丢失
rebias 是否丢失了age?
Process 845 stopped
* thread #5, stop reason = breakpoint 3.1
frame #0: 0x000000010504f980
-> 0x10504f980: popq %rax
0x10504f981: cmpq (%rax), %rax
0x10504f984: xorl %esi, %esi
0x10504f986: movq -0x48(%rbp), %rcx
Target 0: (java) stopped.
(lldb) re r
General Purpose Registers:
rax = 0x0000000747bb8b60
rbx = 0x00000000000000c2
rcx = 0x0000000000000008
rdx = 0x0000000747bb8b80
rdi = 0x0000000101003800
rsi = 0x0000700004a525c8
rbp = 0x0000700004a52678
rsp = 0x0000700004a52628
r8 = 0x0000000000000000
r9 = 0x0000000000000020
r10 = 0x0000000103b0b270 libjvm.dylib`TemplateInterpreter::_active_table + 18432
r11 = 0x00006fff03a50400
r12 = 0x0000000000000000
r13 = 0x000000011cc6b02b
r14 = 0x0000700004a526a8
r15 = 0x0000000101003800
rip = 0x000000010504f980
rflags = 0x0000000000000206
cs = 0x000000000000002b
fs = 0x0000000000000000
gs = 0x0000000000000000
(lldb) p ((oopDesc*)0x0000000747bb8b60)->print()
com.hx.test04.Test26SynchronizeObject
{0x0000000747bb8b60} - 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"{0x0000000747bb8b88} (e8f77171 0)
(lldb) p ((oopDesc*)0x0000000747bb8b60)->age()
(uint) $1 = 0
(lldb) p ((oopDesc*)0x0000000747bb8b60)->incr_age()
(lldb) p ((oopDesc*)0x0000000747bb8b60)->age()
(uint) $2 = 1
(lldb) b 0x10504fa22
Breakpoint 4: address = 0x000000010504fa22
(lldb) c
Process 845 resuming
Process 845 stopped
* thread #5, stop reason = breakpoint 4.1
frame #0: 0x000000010504fa22
-> 0x10504fa22: testq $0x300, %rbx ; imm = 0x300
0x10504fa29: jne 0x10504fa48
0x10504fa2b: andq $0x37f, %rax ; imm = 0x37F
0x10504fa32: movq %rax, %rbx
Target 0: (java) stopped.
(lldb) re r
General Purpose Registers:
rax = 0x000000000000000d
rbx = 0x0000000101003800
rcx = 0x0000000747bb8b60
rdx = 0x0000700004a52630
rdi = 0x0000000101003800
rsi = 0x0000700004a52620
rbp = 0x0000700004a52678
rsp = 0x0000700004a52620
r8 = 0x0000000000000000
r9 = 0x0000000000000020
r10 = 0x0000000103b0b270 libjvm.dylib`TemplateInterpreter::_active_table + 18432
r11 = 0x00006fff03a50400
r12 = 0x0000000000000000
r13 = 0x000000011cc6b02c
r14 = 0x0000700004a526a8
r15 = 0x0000000101003800
rip = 0x000000010504fa22
rflags = 0x0000000000000246
cs = 0x000000000000002b
fs = 0x0000000000000000
gs = 0x0000000000000000
(lldb) register write rbx 0x0000000101003b00
(lldb) stepi
Process 845 stopped
* thread #5, stop reason = instruction step into
frame #0: 0x000000010504fa29
-> 0x10504fa29: jne 0x10504fa48
0x10504fa2b: andq $0x37f, %rax ; imm = 0x37F
0x10504fa32: movq %rax, %rbx
0x10504fa35: orq %r15, %rbx
Target 0: (java) stopped.
(lldb) stepi
Process 845 stopped
* thread #5, stop reason = instruction step into
frame #0: 0x000000010504fa48
-> 0x10504fa48: movl 0x8(%rcx), %ebx
0x10504fa4b: shlq $0x3, %rbx
0x10504fa4f: movq 0xb0(%rbx), %rbx
0x10504fa56: orq %r15, %rbx
Target 0: (java) stopped.
(lldb) stepi -c 3
Process 845 stopped
* thread #5, stop reason = instruction step into
frame #0: 0x000000010504fa56
-> 0x10504fa56: orq %r15, %rbx
0x10504fa59: lock
0x10504fa5a: cmpxchgq %rbx, (%rcx)
0x10504fa5e: jne 0x10504faa5
Target 0: (java) stopped.
(lldb) stepi -c 3
Process 845 stopped
* thread #5, stop reason = instruction step into
frame #0: 0x000000010504fa64
-> 0x10504fa64: jmp 0x10504fd05
0x10504fa69: movl 0x8(%rcx), %ebx
0x10504fa6c: shlq $0x3, %rbx
0x10504fa70: movq 0xb0(%rbx), %rbx
Target 0: (java) stopped.
(lldb) p ((oopDesc*)0x0000000747bb8b60)->age()
(uint) $3 = 0
(lldb) p ((oopDesc*)0x0000000747bb8b60)->mark()->biased_locker()->threadObj()->print()
java.lang.Thread
{0x0000000747f069d8} - klass: 'java/lang/Thread'
- ---- fields (total size 47 words):
- private 'priority' 'I' @12 5
- private 'eetop' 'J' @16 4311758848 (1003800 1)
- private 'stackSize' 'J' @24 0 (0 0)
- private 'nativeParkEventPointer' 'J' @32 0 (0 0)
- private 'tid' 'J' @40 1 (1 0)
- private volatile 'threadStatus' 'I' @48 5
- private 'single_step' 'Z' @52 false
- private 'daemon' 'Z' @53 false
- private 'stillborn' 'Z' @54 false
- private volatile 'name' 'Ljava/lang/String;' @56 "main"{0x0000000747f06b50} (e8fe0d6a 0)
- private 'threadQ' 'Ljava/lang/Thread;' @60 NULL (0 0)
- private 'target' 'Ljava/lang/Runnable;' @64 NULL (0 e8fe0c85)
- private 'group' 'Ljava/lang/ThreadGroup;' @68 a 'java/lang/ThreadGroup'{0x0000000747f06428} (e8fe0c85 e8fc0af7)
- private 'contextClassLoader' 'Ljava/lang/ClassLoader;' @72 a 'jdk/internal/loader/ClassLoaders$AppClassLoader'{0x0000000747e057b8} (e8fc0af7 e8fe0d91)
- private 'inheritedAccessControlContext' 'Ljava/security/AccessControlContext;' @76 a 'java/security/AccessControlContext'{0x0000000747f06c88} (e8fe0d91 e8fe2fb8)
- 'threadLocals' 'Ljava/lang/ThreadLocal$ThreadLocalMap;' @80 a 'java/lang/ThreadLocal$ThreadLocalMap'{0x0000000747f17dc0} (e8fe2fb8 0)
- 'inheritableThreadLocals' 'Ljava/lang/ThreadLocal$ThreadLocalMap;' @84 NULL (0 0)
- volatile 'parkBlocker' 'Ljava/lang/Object;' @88 NULL (0 0)
- private volatile 'blocker' 'Lsun/nio/ch/Interruptible;' @92 NULL (0 e8fe0d70)
- private final 'blockerLock' 'Ljava/lang/Object;' @96 a 'java/lang/Object'{0x0000000747f06b80} (e8fe0d70 0)
- private volatile 'uncaughtExceptionHandler' 'Ljava/lang/Thread$UncaughtExceptionHandler;' @100 NULL (0 0)
- 'threadLocalRandomSeed' 'J' @232 0 (0 0)
- 'threadLocalRandomProbe' 'I' @240 0
- 'threadLocalRandomSecondarySeed' 'I' @244 0
(lldb)
呵呵 rebias 这里就是使用的 ebx, 丢失了 age 的相关信息
进入偏向锁的需求?
1. UseHeavyMonitors 为 false, 从上面的代码可以看出 如果是 UseHeavyMonitors 为true, monitor 只会生成走 InterpreterRuntime::monitorenter 的代码
2. UseBiasedLocking 为 true, 从上面的代码可以看出, 如果 UseBiasedLocking 为 false, 甚至都不会生成 偏向锁部分的处理的代码
3. 创建对象的时候 klass->prototype_header() 为 0x05[bias_pattern 允许偏向锁 0x1, 锁标记为为 0x01][可能影响这个, 参数 BiasedLockingStartupDelay]
添加于2020.07.04 - bytecodeInterpreter.cpp 里面的 monitorenter
呵呵 最近无意间看到了这样的一篇文章 死磕Synchronized底层实现--偏向锁 里面作者介绍的挺好的
另外还有一个收货就是, bytecodeInterpreter.cpp 里面有 monitorenter 的逻辑代码
这个看起来更加友好一些
CASE(_monitorenter): {
oop lockee = STACK_OBJECT(-1);
// derefing's lockee ought to provoke implicit null check
CHECK_NULL(lockee);
// find a free monitor or one already allocated for this object
// if we find a matching object then we need a new monitor
// since this is recursive enter
BasicObjectLock* limit = istate->monitor_base();
BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base();
BasicObjectLock* entry = NULL;
while (most_recent != limit ) {
if (most_recent->obj() == NULL) entry = most_recent;
else if (most_recent->obj() == lockee) break;
most_recent++;
}
if (entry != NULL) {
entry->set_obj(lockee);
int success = false;
uintptr_t epoch_mask_in_place = (uintptr_t)markOopDesc::epoch_mask_in_place;
markOop mark = lockee->mark();
intptr_t hash = (intptr_t) markOopDesc::no_hash;
// implies UseBiasedLocking
if (mark->has_bias_pattern()) {
uintptr_t thread_ident;
uintptr_t anticipated_bias_locking_value;
thread_ident = (uintptr_t)istate->thread();
anticipated_bias_locking_value =
(((uintptr_t)lockee->klass()->prototype_header() | thread_ident) ^ (uintptr_t)mark) &
~((uintptr_t) markOopDesc::age_mask_in_place);
if (anticipated_bias_locking_value == 0) {
// already biased towards this thread, nothing to do
if (PrintBiasedLockingStatistics) {
(* BiasedLocking::biased_lock_entry_count_addr())++;
}
success = true;
}
else if ((anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) != 0) {
// try revoke bias
markOop header = lockee->klass()->prototype_header();
if (hash != markOopDesc::no_hash) {
header = header->copy_set_hash(hash);
}
if (Atomic::cmpxchg_ptr(header, lockee->mark_addr(), mark) == mark) {
if (PrintBiasedLockingStatistics)
(*BiasedLocking::revoked_lock_entry_count_addr())++;
}
}
else if ((anticipated_bias_locking_value & epoch_mask_in_place) !=0) {
// try rebias
markOop new_header = (markOop) ( (intptr_t) lockee->klass()->prototype_header() | thread_ident);
if (hash != markOopDesc::no_hash) {
new_header = new_header->copy_set_hash(hash);
}
if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), mark) == mark) {
if (PrintBiasedLockingStatistics)
(* BiasedLocking::rebiased_lock_entry_count_addr())++;
}
else {
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
}
success = true;
}
else {
// try to bias towards thread in case object is anonymously biased
markOop header = (markOop) ((uintptr_t) mark & ((uintptr_t)markOopDesc::biased_lock_mask_in_place |
(uintptr_t)markOopDesc::age_mask_in_place |
epoch_mask_in_place));
if (hash != markOopDesc::no_hash) {
header = header->copy_set_hash(hash);
}
markOop new_header = (markOop) ((uintptr_t) header | thread_ident);
// debugging hint
DEBUG_ONLY(entry->lock()->set_displaced_header((markOop) (uintptr_t) 0xdeaddead);)
if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), header) == header) {
if (PrintBiasedLockingStatistics)
(* BiasedLocking::anonymously_biased_lock_entry_count_addr())++;
}
else {
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
}
success = true;
}
}
// traditional lightweight locking
if (!success) {
markOop displaced = lockee->mark()->set_unlocked();
entry->lock()->set_displaced_header(displaced);
bool call_vm = UseHeavyMonitors;
if (call_vm || Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced) {
// Is it simple recursive case?
if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {
entry->lock()->set_displaced_header(NULL);
} else {
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
}
}
}
UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
} else {
istate->set_msg(more_monitors);
UPDATE_PC_AND_RETURN(0); // Re-execute
}
}