09 给对象添加偏向锁的调试
前言
锁 这块是 大家在平常的业务开发中基本上都会用到的知识
大家 平常使用到 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 {// identStrprivate String identStr = "xyz";int f01;int f02;int f03;int f04;int f05;// Test25SynchronizeObjectpublic 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_01: invokespecial #1 // Method java/lang/Object."":()V4: aload_05: ldc #2 // String xyz7: putfield #3 // Field identStr:Ljava/lang/String;10: returnpublic static void main(java.lang.String[]) throws java.lang.Exception;Code:0: new #4 // class com/hx/test04/Test26SynchronizeObject3: dup4: invokespecial #5 // Method "":()V7: astore_18: aload_19: dup10: astore_211: monitorenter12: aload_113: invokevirtual #6 // Method java/lang/Object.clone:()Ljava/lang/Object;16: checkcast #4 // class com/hx/test04/Test26SynchronizeObject19: astore_320: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;23: aload_124: getfield #3 // Field identStr:Ljava/lang/String;27: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V30: aload_231: monitorexit32: goto 4235: astore 437: aload_238: monitorexit39: aload 441: athrow42: returnException table:from to target type12 32 35 any35 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/binlldb -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.1frame #0: 0x0000000103923592 libjvm.dylib`::jni_CallStaticVoidMethod(env=0x0000000100807228, cls=0x000000010010a2d0, methodID=0x00000001001b9870) at jni.cpp:1989:211986 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_END1992
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.1frame #0: 0x00000001038eba68 libjvm.dylib`JavaCalls::call_helper(result=0x0000700009373d48, method=0x0000700009373a48, args=0x0000700009373a90, __the_thread__=0x0000000100807000) at javaCalls.cpp:410:7407 { JavaCallWrapper link(method, receiver, result, CHECK);408 { HandleMark hm(thread); // HandleMark used by HandleMarkCleaner409
-> 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.1frame #0: 0x000000010604f980
-> 0x10604f980: popq %rax0x10604f981: cmpq (%rax), %rax0x10604f984: xorl %esi, %esi0x10604f986: movq -0x48(%rbp), %rcx
Target 0: (java) stopped.
(lldb) re r
General Purpose Registers:rax = 0x0000000747bb8ad0rbx = 0x00000000000000c2rcx = 0x0000000000000008rdx = 0x0000000747bb8af0rdi = 0x0000000100803800rsi = 0x0000700002e2b5c8rbp = 0x0000700002e2b678rsp = 0x0000700002e2b628r8 = 0x0000000000000000r9 = 0x0000000000000020r10 = 0x0000000104c0b270 libjvm.dylib`TemplateInterpreter::_active_table + 18432r11 = 0x00006fff0261b000r12 = 0x0000000000000000r13 = 0x000000011e26902br14 = 0x0000700002e2b6a8r15 = 0x0000000100803800rip = 0x000000010604f980rflags = 0x0000000000000206cs = 0x000000000000002bfs = 0x0000000000000000gs = 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 = 0x0000000747bb8b88rbx = 0x00000000000000c2rcx = 0x0000000000000008rdx = 0x0000000747bb8ba8rdi = 0x0000000101800800rsi = 0x000070000c6485c8rbp = 0x000070000c648678rsp = 0x000070000c648628r8 = 0x0000000000000000r9 = 0x0000000000000020r10 = 0x0000000104b0b270 libjvm.dylib`TemplateInterpreter::_active_table + 18432r11 = 0x00006fff0ae46800r12 = 0x0000000000000000r13 = 0x000000011c89102br14 = 0x000070000c6486a8r15 = 0x0000000101800800rip = 0x00000001050cd980rflags = 0x0000000000000206cs = 0x000000000000002bfs = 0x0000000000000000gs = 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 : lockObj0x70000c648630 : 0x70000c648630 : expression bottom0x70000c648638 : 0x011c891024 : byte code pointer0x70000c648640 : 0x70000c6486a8 : pointer to locals0x70000c648648 : 0x011c891108 : constants pool cache0x70000c648650 : 0x0000000000 : method data oop0x70000c648658 : 0x0747bb8668 : java mirror0x70000c648660 : 0x011c891090 : method oop0x70000c648668 : 0x0000000000 : last java stack pointer0x70000c648670 : 0x70000c6486a8 : old stack pointer0x70000c648678 : 0x70000c648710 : old frame pointer0x70000c648680 : 0x010507e9f1 : return address[entry_point's address]0x70000c648688 : 0x0000000000 : ex if exists0x70000c648690 : 0x0000000000 : cloned0x70000c648698 : 0x0000000747bb8b88 : lockObj for monitor enter/exit0x70000c6486a0 : 0x0000000747bb8b88 : lockObj0x70000c6486a8 : 0x0000000747bb8b78 : args0x70000c6486b0 : 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 %rax0x10604f981: cmpq (%rax), %rax// $rsi = 00x10604f984: xorl %esi, %esi// $rcx = $rdx = $expressionBottom0x10604f986: movq -0x48(%rbp), %rcx0x10604f98a: leaq -0x48(%rbp), %rdx0x10604f98e: jmp 0x10604f9a6// BasicLockObject's loop0x10604f990: cmpq $0x0, 0x8(%rcx)0x10604f998: cmoveq %rcx, %rsi0x10604f99c: cmpq 0x8(%rcx), %rax0x10604f9a0: je 0x10604f9ab0x10604f9a2: addq $0x10, %rcx0x10604f9a6: cmpq %rdx, %rcx0x10604f9a9: jne 0x10604f990// if BasicLockObject choosed, goto doLockBiz0x10604f9ab: testq %rsi, %rsi0x10604f9ae: jne 0x10604f9dc// ExpressionStack's move0x10604f9b4: movq -0x48(%rbp), %rsi0x10604f9b8: subq $0x10, %rsp0x10604f9bc: subq $0x10, %rsi0x10604f9c0: movq %rsp, %rcx0x10604f9c3: movq %rsi, -0x48(%rbp)0x10604f9c7: jmp 0x10604f9d70x10604f9cc: movq 0x10(%rcx), %rdx0x10604f9d0: movq %rdx, (%rcx)0x10604f9d3: addq $0x8, %rcx0x10604f9d7: cmpq %rsi, %rcx0x10604f9da: 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// doLockBiz0x10604f9dc: incq %r13// update BasicLockObject.obj = $lockObj0x10604f9df: 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 analysis)来判断到底某个对象是否有引用“逃逸”到某个方法之外,如果没有,就意味着该对象与该方法调用的生命周期一致,就可以将对象分配在栈上。
但这样做非常复杂而且实际好处不够大,所以现在主流JVM也无一实现对象的栈上分配。所以这里就不详细说了,在现实生产中您是不会见到这种做法的。
嘿嘿, 但是这里的 BasicObjectLock 应该算是一种狭义上的 栈上分配吧 ..
lock_object 的业务
interp_masm_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 instructionconst 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 oopconst 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_regmovptr(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 %raxmovl(swap_reg, (int32_t)1);// Load (object->mark() | 1) into swap_reg %raxorptr(swap_reg, Address(obj_reg, 0));// Save (object->mark() | 1) into BasicLock's displaced headermovptr(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 cmpxchgsubptr(swap_reg, rsp);andptr(swap_reg, zero_bits - os::vm_page_size());// Save the test result, for recursive case, the result is zeromovptr(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 casecall_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 objectLabel 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);
#endifif (swap_reg_contains_mark) {null_check_offset = offset();}load_prototype_header(tmp_reg, obj_reg);
#ifdef _LP64orptr(tmp_reg, r15_thread);xorptr(tmp_reg, swap_reg);Register header_reg = tmp_reg;
#elsexorptr(tmp_reg, swap_reg);get_thread(swap_reg);xorptr(swap_reg, tmp_reg);Register header_reg = swap_reg;
#endifandptr(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 _LP64movptr(tmp_reg, swap_reg);orptr(tmp_reg, r15_thread);
#elseget_thread(tmp_reg);orptr(tmp_reg, swap_reg);
#endifif (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 _LP64orptr(tmp_reg, r15_thread);
#elseget_thread(swap_reg);orptr(tmp_reg, swap_reg);movptr(swap_reg, saved_mark_addr);
#endifif (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 3000x11652e151: 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), %rax0x11652e158: movq %rax, %rbx0x11652e15b: andq $0x7, %rbx0x11652e15f: cmpq $0x5, %rbx0x11652e163: jne 0x11652e226// $rbx = obj->klass->prototype_header// $rbx = ¤tThread | $rbx0x11652e169: movl 0x8(%rcx), %ebx0x11652e16c: shlq $0x3, %rbx0x11652e170: movq 0xb0(%rbx), %rbx0x11652e177: 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, %rbx0x11652e181: jne 0x11652e1900x11652e187: pushfq0x11652e188: lock0x11652e189: incl -0xb2934ee(%rip) ; BiasedLocking::_counters + 50x11652e18f: popfq0x11652e190: je 0x11652e4cd// if (lockBits != 0) goto try_revoke_bias0x11652e196: testq $0x7, %rbx0x11652e19d: jne 0x11652e204// if (epoch != 0) goto try_rebias0x11652e19f: testq $0x300, %rbx ; imm = 0x3000x11652e1a6: jne 0x11652e1d4// epoch is valid// 这里使用的是 $eax(obj.mark), 首先是 mask 掉了 rax 的线程的数据, 然后再 写入 $currentThread 0x11652e1a8: andq $0x37f, %rax ; imm = 0x37F0x11652e1af: movq %rax, %rbx0x11652e1b2: orq %r15, %rbx0x11652e1b5: lock0x11652e1b6: cmpxchgq %rbx, (%rcx)// counters != NULL && counters->anonymously_biased_lock_entry_count_addr ++0x11652e1ba: jne 0x11652e1c90x11652e1c0: pushfq0x11652e1c1: lock0x11652e1c2: incl -0xb293523(%rip) ; BiasedLocking::_counters + 90x11652e1c8: popfq0x11652e1c9: jne 0x11652e26d0x11652e1cf: jmp 0x11652e4cd// try_rebias, age 丢了 ?0x11652e1d4: movl 0x8(%rcx), %ebx0x11652e1d7: shlq $0x3, %rbx0x11652e1db: movq 0xb0(%rbx), %rbx0x11652e1e2: orq %r15, %rbx0x11652e1e5: lock0x11652e1e6: cmpxchgq %rbx, (%rcx)// counters != NULL && counters->rebiased_lock_entry_count_addr ++0x11652e1ea: jne 0x11652e1f90x11652e1f0: pushfq0x11652e1f1: lock0x11652e1f2: incl -0xb29354f(%rip) ; BiasedLocking::_counters + 130x11652e1f8: popfq0x11652e1f9: jne 0x11652e26d0x11652e1ff: jmp 0x11652e4cd// try_revoke_bias// obj.set_mark_oop(obj->klass->prototype_header)0x11652e204: movl 0x8(%rcx), %ebx0x11652e207: shlq $0x3, %rbx0x11652e20b: movq 0xb0(%rbx), %rbx0x11652e212: lock0x11652e213: cmpxchgq %rbx, (%rcx)// counters != NULL && counters->revoked_lock_entry_count_addr ++0x11652e217: jne 0x11652e2260x11652e21d: pushfq0x11652e21e: lock0x11652e21f: incl -0xb293578(%rip) ; BiasedLocking::_counters + 170x11652e225: popfq// cmpxchgptr(lock_reg, Address(obj_reg, 0));// 加轻量级锁0x11652e226: movl $0x1, %eax0x11652e22b: orq (%rcx), %rax0x11652e22e: movq %rax, (%rsi)0x11652e231: lock0x11652e232: cmpxchgq %rsi, (%rcx)0x11652e236: jne 0x11652e2450x11652e23c: pushfq0x11652e23d: lock0x11652e23e: incl -0xb293593(%rip) ; BiasedLocking::_counters + 210x11652e244: popfq0x11652e245: je 0x11652e4cd// 轻量级锁重入 0x11652e24b: subq %rsp, %rax0x11652e24e: andq $-0xff9, %rax ; imm = 0xF0070x11652e255: movq %rax, (%rsi)0x11652e258: jne 0x11652e2670x11652e25e: pushfq0x11652e25f: lock0x11652e260: incl -0xb2935b5(%rip) ; BiasedLocking::_counters + 210x11652e266: popfq0x11652e267: je 0x11652e4cd// InterpreterRuntime::monitorenter0x11652e26d: callq 0x11652e2770x11652e272: jmp 0x11652e4cd0x11652e277: leaq 0x8(%rsp), %rax0x11652e27c: movq %r13, -0x40(%rbp)0x11652e280: cmpq $0x0, -0x10(%rbp)0x11652e288: je 0x11652e3050x11652e28e: movq %rsp, -0x28(%rsp)0x11652e293: subq $0x80, %rsp0x11652e29a: 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 = 0x10B0984ED0x11652e2ee: movabsq $0x11652e28e, %rsi ; imm = 0x11652E28E0x11652e2f8: movq %rsp, %rdx0x11652e2fb: andq $-0x10, %rsp0x11652e2ff: callq 0x10ab6af60 ; MacroAssembler::debug64 at macroAssembler_x86.cpp:8620x11652e304: hlt0x11652e305: pushq %r100x11652e307: cmpq -0xb217d1e(%rip), %r12 ; Universe::_narrow_ptrs_base0x11652e30e: je 0x11652e38b0x11652e314: movq %rsp, -0x28(%rsp)0x11652e319: subq $0x80, %rsp0x11652e320: 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 = 0x10B0D4EFC0x11652e374: movabsq $0x11652e314, %rsi ; imm = 0x11652E3140x11652e37e: movq %rsp, %rdx0x11652e381: andq $-0x10, %rsp0x11652e385: callq 0x10ab6af60 ; MacroAssembler::debug64 at macroAssembler_x86.cpp:8620x11652e38a: hlt0x11652e38b: popq %r100x11652e38d: movq %r15, %rdi0x11652e390: movq %rbp, 0x218(%r15)0x11652e397: movq %rax, 0x208(%r15)0x11652e39e: testl $0xf, %esp0x11652e3a4: je 0x11652e3bc0x11652e3aa: subq $0x8, %rsp0x11652e3ae: callq 0x10a8d2360 ; InterpreterRuntime::monitorenter at interpreterRuntime.cpp:6350x11652e3b3: addq $0x8, %rsp0x11652e3b7: jmp 0x11652e3c10x11652e3bc: callq 0x10a8d2360 ; InterpreterRuntime::monitorenter at interpreterRuntime.cpp:6350x11652e3c1: pushq %rax0x11652e3c2: pushq %rdi0x11652e3c3: pushq %rsi0x11652e3c4: pushq %rdx0x11652e3c5: pushq %rcx0x11652e3c6: pushq %r80x11652e3c8: pushq %r90x11652e3ca: pushq %r100x11652e3cc: pushq %r110x11652e3ce: testl $0xf, %esp0x11652e3d4: je 0x11652e3ec0x11652e3da: subq $0x8, %rsp0x11652e3de: callq 0x10a003ae0 ; Thread::current at thread.hpp:6600x11652e3e3: addq $0x8, %rsp0x11652e3e7: jmp 0x11652e3f10x11652e3ec: callq 0x10a003ae0 ; Thread::current at thread.hpp:6600x11652e3f1: popq %r110x11652e3f3: popq %r100x11652e3f5: popq %r90x11652e3f7: popq %r80x11652e3f9: popq %rcx0x11652e3fa: popq %rdx0x11652e3fb: popq %rsi0x11652e3fc: popq %rdi0x11652e3fd: cmpq %rax, %r150x11652e400: je 0x11652e47d0x11652e406: movq %rsp, -0x28(%rsp)0x11652e40b: subq $0x80, %rsp0x11652e412: 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 = 0x10B0D50430x11652e466: movabsq $0x11652e406, %rsi ; imm = 0x11652E4060x11652e470: movq %rsp, %rdx0x11652e473: andq $-0x10, %rsp0x11652e477: callq 0x10ab6af60 ; MacroAssembler::debug64 at macroAssembler_x86.cpp:8620x11652e47c: hlt0x11652e47d: popq %rax0x11652e47e: movabsq $0x0, %r100x11652e488: movq %r10, 0x208(%r15)0x11652e48f: movabsq $0x0, %r100x11652e499: movq %r10, 0x218(%r15)0x11652e4a0: movabsq $0x0, %r100x11652e4aa: movq %r10, 0x210(%r15)0x11652e4b1: cmpq $0x0, 0x8(%r15)0x11652e4b9: je 0x11652e4c40x11652e4bf: jmp 0x1165037a00x11652e4c4: movq -0x40(%rbp), %r130x11652e4c8: movq -0x38(%rbp), %r140x11652e4cc: retq0x11652e4cd: 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.1frame #0: 0x000000010504f980
-> 0x10504f980: popq %rax0x10504f981: cmpq (%rax), %rax0x10504f984: xorl %esi, %esi0x10504f986: movq -0x48(%rbp), %rcx
Target 0: (java) stopped.
(lldb) re r
General Purpose Registers:rax = 0x0000000747bb8b88rbx = 0x00000000000000c2rcx = 0x0000000000000008rdx = 0x0000000747bb8ba8rdi = 0x0000000100805800rsi = 0x0000700007cfc5c8rbp = 0x0000700007cfc678rsp = 0x0000700007cfc628r8 = 0x0000000000000000r9 = 0x0000000000000020r10 = 0x0000000103b0b270 libjvm.dylib`TemplateInterpreter::_active_table + 18432r11 = 0x00006fff074f5400r12 = 0x0000000000000000r13 = 0x000000011c87302br14 = 0x0000700007cfc6a8r15 = 0x0000000100805800rip = 0x000000010504f980rflags = 0x0000000000000206cs = 0x000000000000002bfs = 0x0000000000000000gs = 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.1frame #0: 0x000000010504f9e3
-> 0x10504f9e3: movq 0x8(%rsi), %rcx0x10504f9e7: movq (%rcx), %rax0x10504f9ea: movq %rax, %rbx0x10504f9ed: andq $0x7, %rbx
Target 0: (java) stopped.
(lldb) stepi -c 4
Process 859 stopped
* thread #5, stop reason = instruction step intoframe #0: 0x000000010504f9f1
-> 0x10504f9f1: cmpq $0x5, %rbx0x10504f9f5: jne 0x10504fa7c0x10504f9fb: movl 0x8(%rcx), %ebx0x10504f9fe: shlq $0x3, %rbx
Target 0: (java) stopped.
(lldb) stepi -c 2
Process 859 stopped
* thread #5, stop reason = instruction step intoframe #0: 0x000000010504f9fb
-> 0x10504f9fb: movl 0x8(%rcx), %ebx0x10504f9fe: shlq $0x3, %rbx0x10504fa02: movq 0xb0(%rbx), %rbx0x10504fa09: orq %r15, %rbx
Target 0: (java) stopped.
(lldb) stepi -c 4
Process 859 stopped
* thread #5, stop reason = instruction step intoframe #0: 0x000000010504fa0c
-> 0x10504fa0c: xorq %rax, %rbx0x10504fa0f: andq $-0x79, %rbx0x10504fa13: je 0x10504fd050x10504fa19: testq $0x7, %rbx
Target 0: (java) stopped.
(lldb) stepi -c 2
Process 859 stopped
* thread #5, stop reason = instruction step intoframe #0: 0x000000010504fa13
-> 0x10504fa13: je 0x10504fd050x10504fa19: testq $0x7, %rbx0x10504fa20: jne 0x10504fa690x10504fa22: testq $0x300, %rbx ; imm = 0x300
Target 0: (java) stopped.
(lldb) stepi -c 2
Process 859 stopped
* thread #5, stop reason = instruction step intoframe #0: 0x000000010504fa20
-> 0x10504fa20: jne 0x10504fa690x10504fa22: testq $0x300, %rbx ; imm = 0x300 0x10504fa29: jne 0x10504fa480x10504fa2b: andq $0x37f, %rax ; imm = 0x37F
Target 0: (java) stopped.
(lldb) stepi -c 2
Process 859 stopped
* thread #5, stop reason = instruction step intoframe #0: 0x000000010504fa29
-> 0x10504fa29: jne 0x10504fa480x10504fa2b: andq $0x37f, %rax ; imm = 0x37F 0x10504fa32: movq %rax, %rbx0x10504fa35: orq %r15, %rbx
Target 0: (java) stopped.
(lldb) stepi
Process 859 stopped
* thread #5, stop reason = instruction step intoframe #0: 0x000000010504fa2b
-> 0x10504fa2b: andq $0x37f, %rax ; imm = 0x37F 0x10504fa32: movq %rax, %rbx0x10504fa35: orq %r15, %rbx0x10504fa38: lock
Target 0: (java) stopped.
(lldb) stepi -c 5
Process 859 stopped
* thread #5, stop reason = instruction step intoframe #0: 0x000000010504fa43
-> 0x10504fa43: jmp 0x10504fd050x10504fa48: movl 0x8(%rcx), %ebx0x10504fa4b: shlq $0x3, %rbx0x10504fa4f: 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.1frame #0: 0x000000010504f980
-> 0x10504f980: popq %rax0x10504f981: cmpq (%rax), %rax0x10504f984: xorl %esi, %esi0x10504f986: movq -0x48(%rbp), %rcx
Target 0: (java) stopped.
(lldb) re r
General Purpose Registers:rax = 0x0000000747bb8b60rbx = 0x00000000000000c2rcx = 0x0000000000000008rdx = 0x0000000747bb8b80rdi = 0x0000000101003800rsi = 0x0000700004a525c8rbp = 0x0000700004a52678rsp = 0x0000700004a52628r8 = 0x0000000000000000r9 = 0x0000000000000020r10 = 0x0000000103b0b270 libjvm.dylib`TemplateInterpreter::_active_table + 18432r11 = 0x00006fff03a50400r12 = 0x0000000000000000r13 = 0x000000011cc6b02br14 = 0x0000700004a526a8r15 = 0x0000000101003800rip = 0x000000010504f980rflags = 0x0000000000000206cs = 0x000000000000002bfs = 0x0000000000000000gs = 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.1frame #0: 0x000000010504fa22
-> 0x10504fa22: testq $0x300, %rbx ; imm = 0x300 0x10504fa29: jne 0x10504fa480x10504fa2b: andq $0x37f, %rax ; imm = 0x37F 0x10504fa32: movq %rax, %rbx
Target 0: (java) stopped.
(lldb) re r
General Purpose Registers:rax = 0x000000000000000drbx = 0x0000000101003800rcx = 0x0000000747bb8b60rdx = 0x0000700004a52630rdi = 0x0000000101003800rsi = 0x0000700004a52620rbp = 0x0000700004a52678rsp = 0x0000700004a52620r8 = 0x0000000000000000r9 = 0x0000000000000020r10 = 0x0000000103b0b270 libjvm.dylib`TemplateInterpreter::_active_table + 18432r11 = 0x00006fff03a50400r12 = 0x0000000000000000r13 = 0x000000011cc6b02cr14 = 0x0000700004a526a8r15 = 0x0000000101003800rip = 0x000000010504fa22rflags = 0x0000000000000246cs = 0x000000000000002bfs = 0x0000000000000000gs = 0x0000000000000000(lldb) register write rbx 0x0000000101003b00
(lldb) stepi
Process 845 stopped
* thread #5, stop reason = instruction step intoframe #0: 0x000000010504fa29
-> 0x10504fa29: jne 0x10504fa480x10504fa2b: andq $0x37f, %rax ; imm = 0x37F 0x10504fa32: movq %rax, %rbx0x10504fa35: orq %r15, %rbx
Target 0: (java) stopped.
(lldb) stepi
Process 845 stopped
* thread #5, stop reason = instruction step intoframe #0: 0x000000010504fa48
-> 0x10504fa48: movl 0x8(%rcx), %ebx0x10504fa4b: shlq $0x3, %rbx0x10504fa4f: movq 0xb0(%rbx), %rbx0x10504fa56: orq %r15, %rbx
Target 0: (java) stopped.
(lldb) stepi -c 3
Process 845 stopped
* thread #5, stop reason = instruction step intoframe #0: 0x000000010504fa56
-> 0x10504fa56: orq %r15, %rbx0x10504fa59: 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 intoframe #0: 0x000000010504fa64
-> 0x10504fa64: jmp 0x10504fd050x10504fa69: movl 0x8(%rcx), %ebx0x10504fa6c: shlq $0x3, %rbx0x10504fa70: 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 checkCHECK_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 enterBasicObjectLock* 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 UseBiasedLockingif (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 doif (PrintBiasedLockingStatistics) {(* BiasedLocking::biased_lock_entry_count_addr())++;}success = true;}else if ((anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) != 0) {// try revoke biasmarkOop 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 rebiasmarkOop 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 biasedmarkOop 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 hintDEBUG_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 lockingif (!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}}
完
参考
Hotspot 三种锁实现总结
标志寄存器
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
