前言
09 给对象添加偏向锁的调试 , 接着文章继续调试偏向锁, 我们这里要调试的场景是偏向锁退出的场景
以下内容基于 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_2
13: monitorexit
14: goto 22
17: astore_3
18: aload_2
19: monitorexit
20: aload_3
21: athrow
22: return
Exception table:
from to target type
12 14 17 any
17 20 17 any
}
进入lldb的调试
进入lldb 之后如下, 省略了一部分输出, 不过应该能够看出 当前端点是停在了 javaCalls.cpp -l 410 这里
Process 2543 resuming
Process 2543 stopped
* thread #5, stop reason = breakpoint 1.1
frame #0: 0x0000000103923592 libjvm.dylib`::jni_CallStaticVoidMethod(env=0x0000000101005a28, cls=0x00000001001061d0, methodID=0x0000000104905990) 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 2543 resuming
Process 2543 stopped
* thread #5, stop reason = breakpoint 2.1
frame #0: 0x00000001038eba68 libjvm.dylib`JavaCalls::call_helper(result=0x0000700004af3d48, method=0x0000700004af3a48, args=0x0000700004af3a90, __the_thread__=0x0000000101005800) 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 = 0x000000010584f980 "XH;"
定位一下当前方法, 当前这里正是在调用 main 方法
(lldb) p method()->print()
{method}
- this oop: 0x000000011d36ffd8
- method holder: 'com/hx/test04/Test26SynchronizeObject'
- constants: 0x000000011d36fc68 constant pool [45] {0x000000011d36fc68} for 'com/hx/test04/Test26SynchronizeObject' cache=0x000000011d370050
- access: 0x20000009 public static
- name: 'main'
- signature: '([Ljava/lang/String;)V'
- max stack: 3
- max locals: 4
- size of params: 1
- method size: 11
- vtable index: -2
- i2i entry: 0x000000010582a700
- adapters: AHE@0x00000001018770e0: 0xb0000000 i2c: 0x00000001059a4460 c2i: 0x00000001059a459a c2iUV: 0x00000001059a456d
- compiled entry 0x00000001059a459a
- code size: 23
- code start: 0x000000011d36ff88
- code end (excl): 0x000000011d36ff9f
- checked ex length: 1
- checked ex start: 0x000000011d36ffd4
- linenumber start: 0x000000011d36ff9f
- localvar length: 2
- localvar start: 0x000000011d36ffa8
接着在 monitor_exit 的地方打上一个断点
(lldb) p _active_table._table[9][195]
(address) $1 = 0x000000010504fde0 "XH;"
(lldb) b 0x000000010504fde1
Breakpoint 3: address = 0x000000010504fde1
(lldb) c
Process 2555 resuming
Process 2555 stopped
* thread #5, stop reason = breakpoint 3.1
frame #0: 0x000000010504fde1
-> 0x10504fde1: cmpq (%rax), %rax
0x10504fde4: movq -0x48(%rbp), %rsi
0x10504fde8: leaq -0x48(%rbp), %rdx
0x10504fdec: jmp 0x10504fdfc
Target 0: (java) stopped.
(lldb) re r
General Purpose Registers:
rax = 0x0000000747bb8748
rbx = 0x00000000000000c3
rcx = 0x0000000747bb8748
rdx = 0x0000700003d92638
rdi = 0x0000000104801800
rsi = 0x0000700003d92628
rbp = 0x0000700003d92680
rsp = 0x0000700003d92628
r8 = 0x0000000000000000
r9 = 0x0000000000000020
r10 = 0x0000000103b0aa70 libjvm.dylib`TemplateInterpreter::_active_table + 16384
r11 = 0x00006ffeff58fe00
r12 = 0x0000000000000000
r13 = 0x000000011c96ff95
r14 = 0x0000700003d926a8
r15 = 0x0000000104801800
rip = 0x000000010504fde1
rflags = 0x0000000000000206
cs = 0x000000000000002b
fs = 0x0000000000000000
gs = 0x0000000000000000
(lldb) x 0x000000011c96ff95
0x11c96ff95: c3 a7 00 08 4e 2c c3 2d bf b1 ff 00 2e 42 25 52 ç..N,?-???..B%R
0x11c96ffa5: 00 00 00 00 00 17 00 19 00 1a 00 00 00 00 00 08 ................
(lldb) p ((oopDesc*)0x0000000747bb8748)->print()
com.hx.test04.Test26SynchronizeObject
{0x0000000747bb8748} - 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"{0x0000000747bb8770} (e8f770ee 0)
(lldb) p ((oopDesc*)0x0000000747bb8748)->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 4370470912 (4801800 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
0xc3 表示 monitorenter, 0xa7 表示 goto, 0x4e 表示 astore_3
结合上面的字节码 很容易定位到当前是在执行 monitorexit
并且 lockObj 上面已经添加了 偏向锁
ax 存储的是对象 lockObj
查看一下 栈帧中的信息, 之所以要贴一下 栈帧信息, 是因为 monitorexit 里面的处理需要处理 栈帧中的一部分数据
(lldb) re r
General Purpose Registers:
rax = 0x0000000747bb8748
rbx = 0x00000000000000c3
rcx = 0x0000000747bb8748
rdx = 0x0000700003d92638
rdi = 0x0000000104801800
rsi = 0x0000700003d92628
rbp = 0x0000700003d92680
rsp = 0x0000700003d92628
r8 = 0x0000000000000000
r9 = 0x0000000000000020
r10 = 0x0000000103b0aa70 libjvm.dylib`TemplateInterpreter::_active_table + 16384
r11 = 0x00006ffeff58fe00
r12 = 0x0000000000000000
r13 = 0x000000011c96ff95
r14 = 0x0000700003d926a8
r15 = 0x0000000104801800
rip = 0x000000010504fde1
rflags = 0x0000000000000206
cs = 0x000000000000002b
fs = 0x0000000000000000
gs = 0x0000000000000000
(lldb) x 0x0000700003d92628 -c 0x100
0x700003d92628: 48 87 bb 47 07 00 00 00 48 87 bb 47 07 00 00 00 H.?G....H.?G....
0x700003d92638: 28 26 d9 03 00 70 00 00 94 ff 96 1c 01 00 00 00 (&?..p...?......
0x700003d92648: a8 26 d9 03 00 70 00 00 50 00 97 1c 01 00 00 00 ?&?..p..P.......
0x700003d92658: 00 00 00 00 00 00 00 00 28 82 bb 47 07 00 00 00 ........(.?G....
0x700003d92668: d8 ff 96 1c 01 00 00 00 00 00 00 00 00 00 00 00 ??..............
0x700003d92678: a8 26 d9 03 00 70 00 00 10 27 d9 03 00 70 00 00 ?&?..p...'?..p..
0x700003d92688: f1 09 00 05 01 00 00 00 00 00 00 00 00 00 00 00 ?...............
0x700003d92698: 48 87 bb 47 07 00 00 00 48 87 bb 47 07 00 00 00 H.?G....H.?G....
0x700003d926a8: 38 87 bb 47 07 00 00 00 a0 1f 00 00 03 00 00 00 8.?G....?.......
0x700003d926b8: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x700003d926c8: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x700003d926d8: 00 30 d9 03 00 70 00 00 20 28 d9 03 00 70 00 00 .0?..p.. (?..p..
0x700003d926e8: 50 2d d9 03 00 70 00 00 0a 00 00 00 00 70 00 00 P-?..p.......p..
0x700003d926f8: d8 ff 96 1c 01 00 00 00 00 a7 02 05 01 00 00 00 ??.......?......
0x700003d92708: a0 2a d9 03 00 70 00 00 e0 28 d9 03 00 70 00 00 ?*?..p..?(?..p..
0x700003d92718: 1d bb 0e 03 01 00 00 00 01 00 00 00 00 70 00 00 .?...........p..
0x700003d92628 : 0x0000000747bb8748 : BasicLockObject.lock
0x700003d92630 : 0x0000000747bb8748 : BasicLockObject.obj
0x700003d92638 : 0x700003d92628 : expression bottom
0x700003d92640 : 0x00011c96ff94 : byte code pointer
0x700003d92648 : 0x700003d926a8 : pointer to locals
0x700003d92650 : 0x00011c970050 : constants pool cache
0x700003d92658 : 0x000000000000 : method data oop
0x700003d92660 : 0x0747bb8228 : java mirror
0x700003d92668 : 0x011c96ffd8 : method oop
0x700003d92670 : 0x000000000000 : last java stack pointer
0x700003d92678 : 0x700003d926a8 : old stack pointer
0x700003d92680 : 0x700003d92710 : old frame pointer
0x700003d92688 : 0x01050009f1 : return address[entry_point's address]
0x700003d92690 : 0x000000000000 : ex if exists
0x700003d92698 : 0x0000000747bb8748 : lockObj for monitor enter/exit
0x700003d926a0 : 0x0000000747bb8748 : lockObj
0x700003d926a8 : 0x0000000747bb8738 : args
0x700003d926b0 : 0x0300001fa0 : $mxcsr
查询BasicObjectLock
对应 monitorexit 的汇编的模板代码如下
templateTable_x86.cpp monitorexit
void TemplateTable::monitorexit() {
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;
Register rtop = LP64_ONLY(c_rarg1) NOT_LP64(rdx);
Register rbot = LP64_ONLY(c_rarg2) NOT_LP64(rbx);
Label found;
// find matching slot
{
Label entry, loop;
__ 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 for same object
__ cmpptr(rax, Address(rtop, BasicObjectLock::obj_offset_in_bytes()));
// if same object then stop searching
__ jcc(Assembler::equal, found);
// 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);
}
// error handling. Unlocking was not block-structured
__ call_VM(noreg, CAST_FROM_FN_PTR(address,
InterpreterRuntime::throw_illegal_monitor_state_exception));
__ should_not_reach_here();
// call run-time routine
__ bind(found);
__ push_ptr(rax); // make sure object is on stack (contract with oopMaps)
__ unlock_object(rtop);
__ pop_ptr(rax); // discard object
}
monitorexit 里面 unlock_object 之前对应的汇编如下
lldb) dis -s 0x000000010604fde0 -c 200
0x10604fde0: popq %rax
0x10604fde1: cmpq (%rax), %rax
// rtop = expressionBottom
// rbot = oldExpressionBottom
0x10604fde4: movq -0x48(%rbp), %rsi
0x10604fde8: leaq -0x48(%rbp), %rdx
0x10604fdec: jmp 0x10604fdfc
// BasicLockObject's loop
// if (found) goto 0x1060500d8;
0x10604fdee: cmpq 0x8(%rsi), %rax
0x10604fdf2: je 0x1060500d8
0x10604fdf8: addq $0x10, %rsi
0x10604fdfc: cmpq %rdx, %rsi
0x10604fdff: jne 0x10604fdee
// if (!found) InterpreterRuntime::throw_illegal_monitor_state_exception
0x10604fe01: callq 0x10604fe0b
0x10604fe06: jmp 0x106050061
0x10604fe0b: leaq 0x8(%rsp), %rax
0x10604fe10: movq %r13, -0x40(%rbp)
0x10604fe14: cmpq $0x0, -0x10(%rbp)
0x10604fe1c: je 0x10604fe99
0x10604fe22: movq %rsp, -0x28(%rsp)
0x10604fe27: subq $0x80, %rsp
0x10604fe2e: movq %rax, 0x78(%rsp)
0x10604fe33: movq %rcx, 0x70(%rsp)
0x10604fe38: movq %rdx, 0x68(%rsp)
0x10604fe3d: movq %rbx, 0x60(%rsp)
0x10604fe42: movq %rbp, 0x50(%rsp)
0x10604fe47: movq %rsi, 0x48(%rsp)
0x10604fe4c: movq %rdi, 0x40(%rsp)
0x10604fe51: movq %r8, 0x38(%rsp)
0x10604fe56: movq %r9, 0x30(%rsp)
0x10604fe5b: movq %r10, 0x28(%rsp)
0x10604fe60: movq %r11, 0x20(%rsp)
0x10604fe65: movq %r12, 0x18(%rsp)
0x10604fe6a: movq %r13, 0x10(%rsp)
0x10604fe6f: movq %r14, 0x8(%rsp)
0x10604fe74: movq %r15, (%rsp)
0x10604fe78: movabsq $0x1048984ed, %rdi ; imm = 0x1048984ED
0x10604fe82: movabsq $0x10604fe22, %rsi ; imm = 0x10604FE22
0x10604fe8c: movq %rsp, %rdx
0x10604fe8f: andq $-0x10, %rsp
0x10604fe93: callq 0x10436af60 ; MacroAssembler::debug64 at macroAssembler_x86.cpp:862
0x10604fe98: hlt
0x10604fe99: pushq %r10
0x10604fe9b: cmpq -0x15398b2(%rip), %r12 ; Universe::_narrow_ptrs_base
0x10604fea2: je 0x10604ff1f
0x10604fea8: movq %rsp, -0x28(%rsp)
0x10604fead: subq $0x80, %rsp
0x10604feb4: movq %rax, 0x78(%rsp)
0x10604feb9: movq %rcx, 0x70(%rsp)
0x10604febe: movq %rdx, 0x68(%rsp)
0x10604fec3: movq %rbx, 0x60(%rsp)
0x10604fec8: movq %rbp, 0x50(%rsp)
0x10604fecd: movq %rsi, 0x48(%rsp)
0x10604fed2: movq %rdi, 0x40(%rsp)
0x10604fed7: movq %r8, 0x38(%rsp)
0x10604fedc: movq %r9, 0x30(%rsp)
0x10604fee1: movq %r10, 0x28(%rsp)
0x10604fee6: movq %r11, 0x20(%rsp)
0x10604feeb: movq %r12, 0x18(%rsp)
0x10604fef0: movq %r13, 0x10(%rsp)
0x10604fef5: movq %r14, 0x8(%rsp)
0x10604fefa: movq %r15, (%rsp)
0x10604fefe: movabsq $0x1048d4efc, %rdi ; imm = 0x1048D4EFC
0x10604ff08: movabsq $0x10604fea8, %rsi ; imm = 0x10604FEA8
0x10604ff12: movq %rsp, %rdx
0x10604ff15: andq $-0x10, %rsp
0x10604ff19: callq 0x10436af60 ; MacroAssembler::debug64 at macroAssembler_x86.cpp:862
0x10604ff1e: hlt
0x10604ff1f: popq %r10
0x10604ff21: movq %r15, %rdi
0x10604ff24: movq %rbp, 0x218(%r15)
0x10604ff2b: movq %rax, 0x208(%r15)
0x10604ff32: testl $0xf, %esp
0x10604ff38: je 0x10604ff50
0x10604ff3e: subq $0x8, %rsp
0x10604ff42: callq 0x1040d2a40 ; InterpreterRuntime::throw_illegal_monitor_state_exception at interpreterRuntime.cpp:680
0x10604ff47: addq $0x8, %rsp
0x10604ff4b: jmp 0x10604ff55
0x10604ff50: callq 0x1040d2a40 ; InterpreterRuntime::throw_illegal_monitor_state_exception at interpreterRuntime.cpp:680
0x10604ff55: pushq %rax
0x10604ff56: pushq %rdi
0x10604ff57: pushq %rsi
0x10604ff58: pushq %rdx
0x10604ff59: pushq %rcx
0x10604ff5a: pushq %r8
0x10604ff5c: pushq %r9
0x10604ff5e: pushq %r10
0x10604ff60: pushq %r11
0x10604ff62: testl $0xf, %esp
0x10604ff68: je 0x10604ff80
0x10604ff6e: subq $0x8, %rsp
0x10604ff72: callq 0x103803ae0 ; Thread::current at thread.hpp:660
0x10604ff77: addq $0x8, %rsp
0x10604ff7b: jmp 0x10604ff85
0x10604ff80: callq 0x103803ae0 ; Thread::current at thread.hpp:660
0x10604ff85: popq %r11
0x10604ff87: popq %r10
0x10604ff89: popq %r9
0x10604ff8b: popq %r8
0x10604ff8d: popq %rcx
0x10604ff8e: popq %rdx
0x10604ff8f: popq %rsi
0x10604ff90: popq %rdi
0x10604ff91: cmpq %rax, %r15
0x10604ff94: je 0x106050011
0x10604ff9a: movq %rsp, -0x28(%rsp)
0x10604ff9f: subq $0x80, %rsp
0x10604ffa6: movq %rax, 0x78(%rsp)
0x10604ffab: movq %rcx, 0x70(%rsp)
0x10604ffb0: movq %rdx, 0x68(%rsp)
0x10604ffb5: movq %rbx, 0x60(%rsp)
0x10604ffba: movq %rbp, 0x50(%rsp)
0x10604ffbf: movq %rsi, 0x48(%rsp)
0x10604ffc4: movq %rdi, 0x40(%rsp)
0x10604ffc9: movq %r8, 0x38(%rsp)
0x10604ffce: movq %r9, 0x30(%rsp)
0x10604ffd3: movq %r10, 0x28(%rsp)
0x10604ffd8: movq %r11, 0x20(%rsp)
0x10604ffdd: movq %r12, 0x18(%rsp)
0x10604ffe2: movq %r13, 0x10(%rsp)
0x10604ffe7: movq %r14, 0x8(%rsp)
0x10604ffec: movq %r15, (%rsp)
0x10604fff0: movabsq $0x1048d5043, %rdi ; imm = 0x1048D5043
0x10604fffa: movabsq $0x10604ff9a, %rsi ; imm = 0x10604FF9A
0x106050004: movq %rsp, %rdx
0x106050007: andq $-0x10, %rsp
0x10605000b: callq 0x10436af60 ; MacroAssembler::debug64 at macroAssembler_x86.cpp:862
0x106050010: hlt
0x106050011: popq %rax
0x106050012: movabsq $0x0, %r10
0x10605001c: movq %r10, 0x208(%r15)
0x106050023: movabsq $0x0, %r10
0x10605002d: movq %r10, 0x218(%r15)
0x106050034: movabsq $0x0, %r10
0x10605003e: movq %r10, 0x210(%r15)
0x106050045: cmpq $0x0, 0x8(%r15)
0x10605004d: je 0x106050058
0x106050053: jmp 0x1060007a0
0x106050058: movq -0x40(%rbp), %r13
0x10605005c: movq -0x38(%rbp), %r14
0x106050060: retq
0x106050061: movq %rsp, -0x28(%rsp)
0x106050066: subq $0x80, %rsp
0x10605006d: movq %rax, 0x78(%rsp)
0x106050072: movq %rcx, 0x70(%rsp)
0x106050077: movq %rdx, 0x68(%rsp)
0x10605007c: movq %rbx, 0x60(%rsp)
0x106050081: movq %rbp, 0x50(%rsp)
0x106050086: movq %rsi, 0x48(%rsp)
0x10605008b: movq %rdi, 0x40(%rsp)
0x106050090: movq %r8, 0x38(%rsp)
0x106050095: movq %r9, 0x30(%rsp)
0x10605009a: movq %r10, 0x28(%rsp)
0x10605009f: movq %r11, 0x20(%rsp)
0x1060500a4: movq %r12, 0x18(%rsp)
0x1060500a9: movq %r13, 0x10(%rsp)
0x1060500ae: movq %r14, 0x8(%rsp)
0x1060500b3: movq %r15, (%rsp)
0x1060500b7: movabsq $0x1047f2a69, %rdi ; imm = 0x1047F2A69
0x1060500c1: movabsq $0x106050061, %rsi ; imm = 0x106050061
0x1060500cb: movq %rsp, %rdx
0x1060500ce: andq $-0x10, %rsp
0x1060500d2: callq 0x10436af60 ; MacroAssembler::debug64 at macroAssembler_x86.cpp:862
0x1060500d7: hlt
// unlock_object
0x1060500d8: pushq %rax
0x1060500d9: movq %r13, -0x40(%rbp)
0x1060500dd: leaq (%rsi), %rax
别看这里 贴了这么多汇编出来, 实际核心的处理主要是 BasicLockObject's loop 这一段
查询 BasicLockObject 列表, 寻找 BasicLockObject.obj 为 lockObj 的记录, 然后 交给后面的 unlock_object 处理
如果找不到 表示还没有获取到锁, 抛出 IllegalStateException
unlock_object 的业务
interp_ma**_x86.cpp unlock_object
// Unlocks an object. Used in monitorexit bytecode and
// remove_activation. Throws an IllegalMonitorException if object is
// not locked by current thread.
//
// Args:
// rdx, c_rarg1: BasicObjectLock for lock
//
// Kills:
// rax
// c_rarg0, c_rarg1, c_rarg2, c_rarg3, ... (param regs)
// rscratch1 (scratch reg)
// rax, rbx, rcx, rdx
void InterpreterMacroAssembler::unlock_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::monitorexit),
lock_reg);
} else {
Label done;
const Register swap_reg = rax; // Must use rax for cmpxchg instruction
const Register header_reg = LP64_ONLY(c_rarg2) NOT_LP64(rbx); // Will contain the old oopMark
const Register obj_reg = LP64_ONLY(c_rarg3) NOT_LP64(rcx); // Will contain the oop
save_bcp(); // Save in case of exception
// Convert from BasicObjectLock structure to object and BasicLock
// structure Store the BasicLock address into %rax
lea(swap_reg, Address(lock_reg, BasicObjectLock::lock_offset_in_bytes()));
// Load oop into obj_reg(%c_rarg3)
movptr(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset_in_bytes()));
// Free entry
movptr(Address(lock_reg, BasicObjectLock::obj_offset_in_bytes()), (int32_t)NULL_WORD);
if (UseBiasedLocking) {
biased_locking_exit(obj_reg, header_reg, done);
}
// Load the old header from BasicLock structure
movptr(header_reg, Address(swap_reg,
BasicLock::displaced_header_offset_in_bytes()));
// Test for recursion
testptr(header_reg, header_reg);
// zero for recursive case
jcc(Assembler::zero, done);
// Atomic swap back the old header
if (os::is_MP()) lock();
cmpxchgptr(header_reg, Address(obj_reg, 0));
// zero for simple unlock of a stack-lock case
jcc(Assembler::zero, done);
// Call the runtime routine for slow case.
movptr(Address(lock_reg, BasicObjectLock::obj_offset_in_bytes()),
obj_reg); // restore obj
call_VM(noreg,
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit),
lock_reg);
bind(done);
restore_bcp();
}
}
macroAssembler_x86.cpp biased_locking_exit
void MacroAssembler::biased_locking_exit(Register obj_reg, Register temp_reg, Label& done) {
assert(UseBiasedLocking, "why call this otherwise?");
// Check for biased locking unlock case, which is a no-op
// Note: we do not have to check the thread ID for two reasons.
// First, the interpreter checks for IllegalMonitorStateException at
// a higher level. Second, if the bias was revoked while we held the
// lock, the object could not be rebiased toward another thread, so
// the bias bit would be clear.
movptr(temp_reg, Address(obj_reg, oopDesc::mark_offset_in_bytes()));
andptr(temp_reg, markOopDesc::biased_lock_mask_in_place);
cmpptr(temp_reg, markOopDesc::biased_lock_pattern);
jcc(Assembler::equal, done);
}
unlock_object 这部分生成的汇编如下
(lldb) dis -s 0x1060500d8 -c 200
-> 0x1060500d8: pushq %rax
// 寄存器映射 : swap_reg = rax, obj_reg = rcx, header_reg = rdx
// save_bcp
0x1060500d9: movq %r13, -0x40(%rbp)
// rax = BasicObjectLock.lock
// rcx = BasicObjectLock.obj
// BasicObjectLock.obj = null
0x1060500dd: leaq (%rsi), %rax
0x1060500e0: movq 0x8(%rsi), %rcx
0x1060500e4: movq $0x0, 0x8(%rsi)
// biased_locking_exit
// if(obj.mark().has_bias_pattern()) goto done;
0x1060500ec: movq (%rcx), %rdx
0x1060500ef: andq $0x7, %rdx
0x1060500f3: cmpq $0x5, %rdx
0x1060500f7: je 0x106050378
// rdx = BasicObjectLock.lock.displaced_header
// if(BasicObjectLock.lock.displaced_header == null) goto done;
0x1060500fd: movq (%rax), %rdx
0x106050100: testq %rdx, %rdx
0x106050103: je 0x106050378
// if(cas(rdx, lockObj.mark_addr, rax)) goto done;
0x106050109: lock
0x10605010a: cmpxchgq %rdx, (%rcx)
0x10605010e: je 0x106050378
// BasicObjectLock.obj = lockObj
// InterpreterRuntime::monitorexit
0x106050114: movq %rcx, 0x8(%rsi)
0x106050118: callq 0x106050122
0x10605011d: jmp 0x106050378
0x106050122: leaq 0x8(%rsp), %rax
0x106050127: movq %r13, -0x40(%rbp)
0x10605012b: cmpq $0x0, -0x10(%rbp)
0x106050133: je 0x1060501b0
0x106050139: movq %rsp, -0x28(%rsp)
0x10605013e: subq $0x80, %rsp
0x106050145: movq %rax, 0x78(%rsp)
0x10605014a: movq %rcx, 0x70(%rsp)
0x10605014f: movq %rdx, 0x68(%rsp)
0x106050154: movq %rbx, 0x60(%rsp)
0x106050159: movq %rbp, 0x50(%rsp)
0x10605015e: movq %rsi, 0x48(%rsp)
0x106050163: movq %rdi, 0x40(%rsp)
0x106050168: movq %r8, 0x38(%rsp)
0x10605016d: movq %r9, 0x30(%rsp)
0x106050172: movq %r10, 0x28(%rsp)
0x106050177: movq %r11, 0x20(%rsp)
0x10605017c: movq %r12, 0x18(%rsp)
0x106050181: movq %r13, 0x10(%rsp)
0x106050186: movq %r14, 0x8(%rsp)
0x10605018b: movq %r15, (%rsp)
0x10605018f: movabsq $0x1040984ed, %rdi ; imm = 0x1040984ED
0x106050199: movabsq $0x106050139, %rsi ; imm = 0x106050139
0x1060501a3: movq %rsp, %rdx
0x1060501a6: andq $-0x10, %rsp
0x1060501aa: callq 0x103b6af60 ; MacroAssembler::debug64 at macroAssembler_x86.cpp:862
0x1060501af: hlt
0x1060501b0: pushq %r10
0x1060501b2: cmpq -0x1d39bc9(%rip), %r12 ; Universe::_narrow_ptrs_base
0x1060501b9: je 0x106050236
0x1060501bf: movq %rsp, -0x28(%rsp)
0x1060501c4: subq $0x80, %rsp
0x1060501cb: movq %rax, 0x78(%rsp)
0x1060501d0: movq %rcx, 0x70(%rsp)
0x1060501d5: movq %rdx, 0x68(%rsp)
0x1060501da: movq %rbx, 0x60(%rsp)
0x1060501df: movq %rbp, 0x50(%rsp)
0x1060501e4: movq %rsi, 0x48(%rsp)
0x1060501e9: movq %rdi, 0x40(%rsp)
0x1060501ee: movq %r8, 0x38(%rsp)
0x1060501f3: movq %r9, 0x30(%rsp)
0x1060501f8: movq %r10, 0x28(%rsp)
0x1060501fd: movq %r11, 0x20(%rsp)
0x106050202: movq %r12, 0x18(%rsp)
0x106050207: movq %r13, 0x10(%rsp)
0x10605020c: movq %r14, 0x8(%rsp)
0x106050211: movq %r15, (%rsp)
0x106050215: movabsq $0x1040d4efc, %rdi ; imm = 0x1040D4EFC
0x10605021f: movabsq $0x1060501bf, %rsi ; imm = 0x1060501BF
0x106050229: movq %rsp, %rdx
0x10605022c: andq $-0x10, %rsp
0x106050230: callq 0x103b6af60 ; MacroAssembler::debug64 at macroAssembler_x86.cpp:862
0x106050235: hlt
0x106050236: popq %r10
0x106050238: movq %r15, %rdi
0x10605023b: movq %rbp, 0x218(%r15)
0x106050242: movq %rax, 0x208(%r15)
0x106050249: testl $0xf, %esp
0x10605024f: je 0x106050267
0x106050255: subq $0x8, %rsp
0x106050259: callq 0x1038d2700 ; InterpreterRuntime::monitorexit at interpreterRuntime.cpp:660
0x10605025e: addq $0x8, %rsp
0x106050262: jmp 0x10605026c
0x106050267: callq 0x1038d2700 ; InterpreterRuntime::monitorexit at interpreterRuntime.cpp:660
0x10605026c: pushq %rax
0x10605026d: pushq %rdi
0x10605026e: pushq %rsi
0x10605026f: pushq %rdx
0x106050270: pushq %rcx
0x106050271: pushq %r8
0x106050273: pushq %r9
0x106050275: pushq %r10
0x106050277: pushq %r11
0x106050279: testl $0xf, %esp
0x10605027f: je 0x106050297
0x106050285: subq $0x8, %rsp
0x106050289: callq 0x103003ae0 ; Thread::current at thread.hpp:660
0x10605028e: addq $0x8, %rsp
0x106050292: jmp 0x10605029c
0x106050297: callq 0x103003ae0 ; Thread::current at thread.hpp:660
0x10605029c: popq %r11
0x10605029e: popq %r10
0x1060502a0: popq %r9
0x1060502a2: popq %r8
0x1060502a4: popq %rcx
0x1060502a5: popq %rdx
0x1060502a6: popq %rsi
0x1060502a7: popq %rdi
0x1060502a8: cmpq %rax, %r15
0x1060502ab: je 0x106050328
0x1060502b1: movq %rsp, -0x28(%rsp)
0x1060502b6: subq $0x80, %rsp
0x1060502bd: movq %rax, 0x78(%rsp)
0x1060502c2: movq %rcx, 0x70(%rsp)
0x1060502c7: movq %rdx, 0x68(%rsp)
0x1060502cc: movq %rbx, 0x60(%rsp)
0x1060502d1: movq %rbp, 0x50(%rsp)
0x1060502d6: movq %rsi, 0x48(%rsp)
0x1060502db: movq %rdi, 0x40(%rsp)
0x1060502e0: movq %r8, 0x38(%rsp)
0x1060502e5: movq %r9, 0x30(%rsp)
0x1060502ea: movq %r10, 0x28(%rsp)
0x1060502ef: movq %r11, 0x20(%rsp)
0x1060502f4: movq %r12, 0x18(%rsp)
0x1060502f9: movq %r13, 0x10(%rsp)
0x1060502fe: movq %r14, 0x8(%rsp)
0x106050303: movq %r15, (%rsp)
0x106050307: movabsq $0x1040d5043, %rdi ; imm = 0x1040D5043
0x106050311: movabsq $0x1060502b1, %rsi ; imm = 0x1060502B1
0x10605031b: movq %rsp, %rdx
0x10605031e: andq $-0x10, %rsp
0x106050322: callq 0x103b6af60 ; MacroAssembler::debug64 at macroAssembler_x86.cpp:862
0x106050327: hlt
0x106050328: popq %rax
0x106050329: movabsq $0x0, %r10
0x106050333: movq %r10, 0x208(%r15)
0x10605033a: movabsq $0x0, %r10
0x106050344: movq %r10, 0x218(%r15)
0x10605034b: movabsq $0x0, %r10
0x106050355: movq %r10, 0x210(%r15)
0x10605035c: cmpq $0x0, 0x8(%r15)
0x106050364: je 0x10605036f
0x10605036a: jmp 0x1060007a0
0x10605036f: movq -0x40(%rbp), %r13
0x106050373: movq -0x38(%rbp), %r14
0x106050377: retq
// done;
0x106050378: movq -0x40(%rbp), %r13
0x10605037c: popq %rax
0x10605037d: movzbl 0x1(%r13), %ebx
0x106050382: incq %r13
0x106050385: movabsq $0x10430b270, %r10 ; imm = 0x10430B270
0x10605038f: jmpq *(%r10,%rbx,8)
0x106050393: nop
0x106050394: nop
通过以上的汇编代码块, 可以整理出一些规则
1. 如果 obj 上面是偏向锁, 那么什么都不用做, 直接 done
2. 如果 obj 对应的 BasicObjectLock.lock.displaced_header 为 null(轻量级锁重入的情况), 直接 done
3. 如果 obj 对应的 locker 为 BasicObjectLock.lock, cas 替换其 mark 为 BasicObjectLock.lock.displaced_header 当前 直接 done
4. 走 InterpreterRuntime::monitorexit
我们这里的偏向锁退出, 直接走的是 1, 检查了一下锁标记位和偏向锁标记位, 然后done
* thread #5, stop reason = breakpoint 4.1
frame #0: 0x00000001060500ec
-> 0x1060500ec: movq (%rcx), %rdx
0x1060500ef: andq $0x7, %rdx
0x1060500f3: cmpq $0x5, %rdx
0x1060500f7: je 0x106050378
Target 0: (java) stopped.
(lldb) re r
General Purpose Registers:
rax = 0x0000700006a3c628
rbx = 0x00000000000000c3
rcx = 0x0000000747bb86a0
rdx = 0x0000700006a3c638
rdi = 0x0000000101003800
rsi = 0x0000700006a3c628
rbp = 0x0000700006a3c680
rsp = 0x0000700006a3c620
r8 = 0x0000000000000000
r9 = 0x0000000000000020
r10 = 0x0000000104b0aa70 libjvm.dylib`TemplateInterpreter::_active_table + 16384
r11 = 0x00006fff05a37c00
r12 = 0x0000000000000000
r13 = 0x000000011e267f95
r14 = 0x0000700006a3c6a8
r15 = 0x0000000101003800
rip = 0x00000001060500ec
rflags = 0x0000000000000246
cs = 0x000000000000002b
fs = 0x0000000000000000
gs = 0x0000000000000000
(lldb) x 0x0000000747bb86a0
0x747bb86a0: 05 38 00 01 01 00 00 00 86 1f 01 f8 00 00 00 00 .8.........?....
0x747bb86b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
(lldb) stepi
Process 2662 stopped
* thread #5, stop reason = instruction step into
frame #0: 0x00000001060500ef
-> 0x1060500ef: andq $0x7, %rdx
0x1060500f3: cmpq $0x5, %rdx
0x1060500f7: je 0x106050378
0x1060500fd: movq (%rax), %rdx
Target 0: (java) stopped.
(lldb) stepi
Process 2662 stopped
* thread #5, stop reason = instruction step into
frame #0: 0x00000001060500f3
-> 0x1060500f3: cmpq $0x5, %rdx
0x1060500f7: je 0x106050378
0x1060500fd: movq (%rax), %rdx
0x106050100: testq %rdx, %rdx
Target 0: (java) stopped.
(lldb) stepi
Process 2662 stopped
* thread #5, stop reason = instruction step into
frame #0: 0x00000001060500f7
-> 0x1060500f7: je 0x106050378
0x1060500fd: movq (%rax), %rdx
0x106050100: testq %rdx, %rdx
0x106050103: je 0x106050378
Target 0: (java) stopped.
(lldb) stepi
Process 2662 stopped
* thread #5, stop reason = instruction step into
frame #0: 0x0000000106050378
-> 0x106050378: movq -0x40(%rbp), %r13
0x10605037c: popq %rax
0x10605037d: movzbl 0x1(%r13), %ebx
0x106050382: incq %r13
Target 0: (java) stopped.
(lldb) p ((oopDesc*)0x0000000747bb86a0)->print()
com.hx.test04.Test26SynchronizeObject
{0x0000000747bb86a0} - klass: 'com/hx/test04/Test26SynchronizeObject'
- ---- fields (total size 5 words):
- 'f01' 'I' @12 0
- 'f02' 'I' @16 0
- 'f03' 'I' @20 0
- 'f04' 'I' @24 0
- 'f05' 'I' @28 0
- private 'identStr' 'Ljava/lang/String;' @32 "xyz"{0x0000000747bb86c8} (e8f770d9 0)
(lldb) p ((oopDesc*)0x0000000747bb86a0)->mark()->has_bias_pattern()
(bool) $2 = true
(lldb) p ((oopDesc*)0x0000000747bb86a0)->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