目录

  • 对于可偏向、偏向锁、无锁、轻量锁、重量锁源码级解析
  • 一、不同修饰的区别
    • 1、修饰方法
    • 2、修饰代码块
  • 二、synchronized通用逻辑lock_object函数
    • 1、biased_locking_enter函数-偏向锁
      • 1.1、 无锁可偏向,怎么理解?
      • 1.2、偏向锁
    • 2、轻量|重量锁
      • 函数lock_object
        • lock_object小结:
      • 函数 InterpreterRuntime::monitorenter
        • monitorenter小结:
  • 三、锁消除 monitorexit
  • 总结
    • 上锁:
      • 偏向锁:
      • 轻量锁 | 重量锁:
        • 轻量锁:
        • 重量锁:
    • 解锁:

对于可偏向、偏向锁、无锁、轻量锁、重量锁源码级解析

我的环境 ubuntu 64 jdk1.8+ clion、gdb
这里简单介绍一下一些基础知识
1、锁状态位,markword或者_prototype_header的后三位,用org.openjdk.jol包查看就是高8位的第三位
2、其他位,先不管分代年龄和epoch那其他位就表示是偏向线程或上锁线程

--------------------------------正题开始(华丽丽得分割线)----------------------------------------------------

在分析锁synchronized前先要知道markword在jvm的类叫oopDesc。它存在两个地方,而且这两个地方指向的内存还不是同一块。其中一个就是我们俗称对象存,在oopDesc这个类中(oopDesc有很多继承类这里不展开),也就是我们常说的放在堆空间的对象;另一个就是klass(同样很多继承它的,不展开)存在元空间(klass-oop二分模型)。在oop中markOopDesc的变量名叫_mark,在kalss中变量名叫_prototype_header

  typedef class   markOopDesc*                markOop;//oopDesc中的定义volatile markOop  _mark;
//klass中的定义markOop  _prototype_header;   // Used when biased locking is both enabled and disabled for this type

一、不同修饰的区别

1、修饰方法

synchronized修饰为方法和代码块这两种申明创建的ObjectMonitor时机不同。下面的代码和汇编是修饰方法的。

//源码
public class Test{public static synchronized int  exp(int t ) throws InterruptedException {return 0;}public static void main(String[] args) throws InterruptedException {exp(1);}
}//exp方法的字节码
Classfile /home/ziya/Desktop/Test.classLast modified 2022-12-17; size 394 bytesMD5 checksum ed0edf2dd2f862dbd54dd1820b813ed4Compiled from "Test.java"
public class Testminor version: 0major version: 52flags: ACC_PUBLIC, ACC_SUPER
Constant pool:#1 = Methodref          #4.#17         // java/lang/Object."<init>":()V#2 = Methodref          #3.#18         // Test.exp:(I)I#3 = Class              #19            // Test#4 = Class              #20            // java/lang/Object#5 = Utf8               <init>#6 = Utf8               ()V#7 = Utf8               Code#8 = Utf8               LineNumberTable#9 = Utf8               exp#10 = Utf8               (I)I#11 = Utf8               Exceptions#12 = Class              #21            // java/lang/InterruptedException#13 = Utf8               main#14 = Utf8               ([Ljava/lang/String;)V#15 = Utf8               SourceFile#16 = Utf8               Test.java#17 = NameAndType        #5:#6          // "<init>":()V#18 = NameAndType        #9:#10         // exp:(I)I#19 = Utf8               Test#20 = Utf8               java/lang/Object#21 = Utf8               java/lang/InterruptedException
{public Test();descriptor: ()Vflags: ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 2: 0public static synchronized int exp(int) throws java.lang.InterruptedException;descriptor: (I)Iflags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZEDCode:stack=1, locals=1, args_size=10: iconst_01: ireturnLineNumberTable:line 5: 0Exceptions:throws java.lang.InterruptedExceptionpublic static void main(java.lang.String[]) throws java.lang.InterruptedException;descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=1, locals=1, args_size=10: iconst_11: invokestatic  #2                  // Method exp:(I)I4: pop5: returnLineNumberTable:line 9: 0line 10: 5Exceptions:throws java.lang.InterruptedException
}

修饰方法详细讲会有点冗长简单的说一下理解就行先上一段源码

address AbstractInterpreterGenerator::generate_method_entry(AbstractInterpreter::MethodKind kind) {bool synchronized = false;address entry_point = NULL;InterpreterGenerator* ig_this = (InterpreterGenerator*)this;switch (kind) {case Interpreter::zerolocals             :              /** zerolocals表示普通方法类型 **/                                break;case Interpreter::zerolocals_synchronized: synchronized = true;  /** zerolocals表示普通的、同步方法类型 **/                 break;//.......................................省略好多代码.........................................}return ig_this->generate_normal_entry(synchronized);
}

jvm通执行java方法内的字节码前需要执行entry_point的执行流(可以理解为执行java方法前的通用逻辑(申请栈、栈帧,生成局部变量表等操作。具体的就不展开了),jvm在启动是时调用上面这段源码中的函数来构建entry_point执行流,entry_point执行流有很多种例如native_entry、abstract_entry。被synchronized修饰的普通方法这里只要关心上面源码中的两种。接着在generate_normal_entry函数中写入对应的硬编码,其中有一段

address InterpreterGenerator::generate_normal_entry(bool synchronized) {//.......................................省略好多代码..........................................if (synchronized) {lock_method();}//.......................................省略好多代码..........................................
}//
void InterpreterGenerator::lock_method(void) {// synchronize methodconst Address access_flags      (rbx, Method::access_flags_offset());const Address monitor_block_top (rbp, frame::interpreter_frame_monitor_block_top_offset * wordSize);const int entry_size            = frame::interpreter_frame_monitor_size() * wordSize;
//...........................................省略一些ASSERT.................................................// get synchronization object{ Label done;const int mirror_offset = in_bytes(Klass::java_mirror_offset());__ movl(rax, access_flags);__ testl(rax, JVM_ACC_STATIC);__ movptr(rax, Address(rdi, Interpreter::local_offset_in_bytes(0)));  // get receiver (assume this is frequent case)__ jcc(Assembler::zero, done);__ movptr(rax, Address(rbx, Method::const_offset()));__ movptr(rax, Address(rax, ConstMethod::constants_offset()));__ movptr(rax, Address(rax, ConstantPool::pool_holder_offset_in_bytes()));__ movptr(rax, Address(rax, mirror_offset));//...........................................省略一些ASSERT.................................................__ bind(done);}// add space for monitor & lock__ subptr(rsp, entry_size);                                           // add space for a monitor entry__ movptr(monitor_block_top, rsp);                                    // set new monitor block top__ movptr(Address(rsp, BasicObjectLock::obj_offset_in_bytes()), rax); // store object__ mov(rdx, rsp);                                                    // object address__ lock_object(rdx);
}

这就意味着如果使用的synchronized为true的entry_point必定先通过lock_method(void)函数里生成的对应的硬编码,那就解析一下lock_method这个函数生成的汇编

//lock_method对应的汇编
//...........................................省略一些ASSERT.................................................
//先找到java方法的access_flags demo的字节码中exp方法的修饰为
//flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED三个,对应jvm的三个值按位或计算位21
//#define JVM_ACC_PUBLIC        0x0001
//#define JVM_ACC_STATIC        0x0008
//#define JVM_ACC_SYNCHRONIZED  0x0020  mov    0x28(%rbx),%eax      //__ movl(rax, access_flags);   test   $0x8,%eax            //__ testl(rax, JVM_ACC_STATIC);是否仅为static方法mov    (%r14),%rax          //__ movptr(rax, Address(rdi, Interpreter::local_offset_in_bytes(0)))获取局部变量的一个参数je     bind(done)           //仅为static则跳转//从rbx寄存器中存的是Method指针mov    0x10(%rbx),%rax        //__ movptr(rax, Address(rbx, Method::const_offset()))  Method*+0x10指向ConstMethod*mov    0x8(%rax),%rax        //__ movptr(rax, Address(rax, ConstMethod::constants_offset()))  ConstMethod*+0x8指向ConstantPool*mov    0x20(%rax),%rax     //__ movptr(rax, Address(rax, ConstantPool::pool_holder_offset_in_bytes()))  ConstantPool*+0x20指向_pool_holder该变量是一个InstanceKlass指针,也就是当前常量池的持有类mov    0x70(%rax),%rax       //__ movptr(rax, Address(rax, mirror_offset))  InstanceKlass*+0x70指当前java类class对应jvm的klass的中镜像oop  _java_mirror
//...........................................省略一些ASSERT.................................................
//  __ bind(done);sub    $0x10,%rsp         //提升16字节的栈空间存放lock record用mov    %rsp,-0x40(%rbp)       //将提升后的栈地址存入rbp - 0x40的位置 占用lock record中lock位置mov    %rax,0x8(%rsp)     //将当前类的oop也就是上面的oop  _java_mirror存入,占用lock record中oop位置mov    %rsp,%rsi          //将lock record首地址暂存rsi寄存器class BasicObjectLock VALUE_OBJ_CLASS_SPEC {//lock record的原型private:BasicLock _lock;                                    // the lock, must be double word alignedoop       _obj;                                     // object holds the lock;               }

lock_method函数结束。总结一下,这里只变更了两个地方:rax寄存器,存着镜像对象oop;栈空间提升了0x10字节,存着lock record
接着分析lock_object(rdx)函数

2、修饰代码块

//字节码monitorenter的源码
void TemplateTable::monitorenter() {transition(atos, vtos);// check for NULL object/**  cmp    (%rax),%rax   **/__ 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;// initialize entry pointer/**  xor    %esi,%esi   **/__ xorl(c_rarg1, c_rarg1); // points to free slot or NULL 清零 释放rsi寄存器// find a free slot in the monitor block (result in c_rarg1){Label entry, loop, exit;/**  mov    -0x40(%rbp),%rcx   **/__ movptr(c_rarg3, monitor_block_top); /**  lea    -0x40(%rbp),%rdx   **/__ lea(c_rarg2, monitor_block_bot); // 获得lock record中ObjectMonitor起始地址
//跳转 /**  jmp    bind(entry)       **/__ jmpb(entry);
/* |                            //这一段简单说就是循环对比多个lockrecord中的oop与当前要上锁的oop是否相同,找到当前要操作的lockrecord
/* |    */  __ bind(loop);                                                                                                  //跳这┐
/* |   cmpq   $0x0,0x8(%rcx)     **/__ cmpptr(Address(c_rarg3, BasicObjectLock::obj_offset_in_bytes()), (int32_t) NULL_WORD);  // |
/* |   cmove  %rcx,%rsi          **/__ cmov(Assembler::equal, c_rarg1, c_rarg3);                                               // |
/* |   cmp    0x8(%rcx),%rax     **/__ cmpptr(rax, Address(c_rarg3, BasicObjectLock::obj_offset_in_bytes()));                  // |
/* |   je     bind(exit)         **/__ jccb(Assembler::equal, exit);                                                           // |
/* |   add    $0x10,%rcx         **/__ addptr(c_rarg3, entry_size);                                                            // |
/* └跳到这 */                                                                                                                 // |__ bind(entry);                                                                                                             // |// check if bottom reachedv                                                                                                 // |/**  cmp    %rdx,%rcx          **/__ cmpptr(c_rarg3, c_rarg2); //通过mov[]和lea命令获得的值做对是否相同,                    // |/** rcx和rdx 不相同的场景在上面偏向锁的时候说解析过了**/                                                                     // |/**  jne    bind(loop)          **/__ jcc(Assembler::notEqual, loop); //zf位不为0则跳到上面循环一下                         // ┘__ bind(exit);}/**  test   %rsi,%rsi            **/__ testptr(c_rarg1, c_rarg1);          // rsi寄存器未经loop是0,下面jcc不跳转做上锁前准备。// 经过loop的rsi可能已经赋值一个oop,这里就得跳转了,重量锁上锁后解锁,不会回收oop中的ObjectMonitor指针/**  jne    0x7fa2710458dc       **/__ jcc(Assembler::notZero, allocated); // allocate one if there's no free slot{Label entry, loop,skip;//jvm原注释:1. compute new pointers 其实就是提升16字节的栈,当lock record用。-0x40(%rbp)就是lock record的首地址/**  mov    -0x40(%rbp),%rsi   **/__ movptr(c_rarg1, monitor_block_bot); // c_rarg1: old expression stack bottom/**  sub    $0x10,%rsp         **/__ subptr(rsp, entry_size);            //rsp-0x10是开辟新的lockrecord/**  sub    $0x10,%rsi         **/__ subptr(c_rarg1, entry_size);        //rsi-0x10是根据已有lockrecord的地址再次开辟一个lockrecord/**  mov    %rsp,%rcx          **/__ mov(c_rarg3, rsp);                  // set start value for copy loop/**  mov    %rsi,-0x40(%rbp)   **/__ movptr(monitor_block_bot, c_rarg1); // set new monitor block bottom/**  jmpq   0x7fa2710458d7     **/__ jmp(entry); //跳转到bind(entry)处//jvm原注释: 2. move expression stack contents 这一段是在有多个lockrecord的情况下,重新整理栈__ bind(loop);/**  mov    0x10(%rcx),%rdx    **/__ movptr(c_rarg2, Address(c_rarg3, entry_size)); //当前rcx指向栈顶,也就是lockrecord中的指向lock的栈指针/**  mov    %rdx,(%rcx)        **/__ movptr(Address(c_rarg3, 0), c_rarg2);          // and store it at new location/**  add    $0x8,%rcx          **/__ addptr(c_rarg3, wordSize);                     // advance to next word__ bind(entry);/**  cmp    %rsi,%rcx          **/__ cmpptr(c_rarg3, c_rarg1);            // check if bottom reached/**  jne    0x7fa2710458cc     **/__ jcc(Assembler::notEqual, loop);      // if not at bottom then// copy next word}// call run-time routine// c_rarg1: 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./**  inc    %r13                 **/__ increment(r13);// store object 先oop写入lockrecord/**  mov    %rax,0x8(%rsi)       **/__ movptr(Address(c_rarg1, BasicObjectLock::obj_offset_in_bytes()), rax);__ lock_object(c_rarg1);// 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);
}

-------------------------------------------------------华丽得分割线-------------------------------------------------------------------

二、synchronized通用逻辑lock_object函数

无论是那种synchronized修饰,最终上锁的逻辑都是通过这个函数来操作的

void InterpreterMacroAssembler::lock_object(Register lock_reg) {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 obj_reg  = 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;movptr(obj_reg, Address(lock_reg, obj_offset)); //mov    0x8(%rsi),%rcxif (UseBiasedLocking) {biased_locking_enter(lock_reg, obj_reg, swap_reg, noreg, false, done, &slow_case);}
............................................................................bind(done);}
}

1、biased_locking_enter函数-偏向锁

偏向锁有两种状态1、无锁可偏向和2、已有线程的偏向锁。
klass中_prototype_header的偏向在延迟偏向调度时,这个下面在细讲。这里看一下klass创建时初始化prototype_header
Klass::Klass() {.........................................................set_prototype_header(markOopDesc::prototype());
.........................................................
}

oopDesc的_mark是在new字节码中创建oop时分配的

//new字节码中定义oopDesc/**  sub    $0x10,%edx            **/__ decrementl(rdx, sizeof(oopDesc));            //对象所需大小和对象头大小一样表明对象真正的实例数据内存为0,那么就不需要进行对象实例数据的初始化了,直接跳往对象头初始化处即可。/**  je   bind(initialize_header) **/__ jcc(Assembler::zero, initialize_header);     //Hotspot中虽然对象头在内存中排在对象实例数据前,但是会先初始化对象实例数据,再初始化对象头。............................................................................/**  mov    %rcx,0x8(%rax,%rdx,8)**/__ movq(Address(rax, rdx, Address::times_8,sizeof(oopDesc) - oopSize),rcx);/**  dec    %edx                **/__ decrementl(rdx);__ jcc(Assembler::notZero, loop);}// initialize object header only.__ bind(initialize_header);if (UseBiasedLocking) {//将类的偏向锁相关数据移动到对象头 需要注意的是这里的锁状态位是设置到堆区oop对象头的而不是klass的prototype_header中/**  mov    0xb0(%rsi),%r10   **/__ movptr(rscratch1, Address(rsi, Klass::prototype_header_offset()));/**  mov    %r10,(%rax)       **/__ movptr(Address(rax, oopDesc::mark_offset_in_bytes()), rscratch1);} else {//未开启偏向锁__ movptr(Address(rax, oopDesc::mark_offset_in_bytes()),(intptr_t) markOopDesc::prototype()); // header (address 0x1)}

所以说,jdk1.8下,通过openjdk.jol查看对象或者,在延迟偏移执行前无论是创建对象还是查看class,偏向锁位就是0,即无锁01状态

//偏向前0     4    (object header)    01 00 00 00 (00000001 00000000 00000000 00000000) (1)4     4    (object header)    00 00 00 00 (00000000 00000000 00000000 00000000) (0)8     4    (object header)    df 03 00 f8 (11011111 00000011 00000000 11111000) (-134216737)
//偏向后0     4    (object header)    05 00 00 00 (00000101 00000000 00000000 00000000) (5)4     4    (object header)    00 00 00 00 (00000000 00000000 00000000 00000000) (0)8     4    (object header)    df 03 00 f8 (11011111 00000011 00000000 11111000) (-134216737)

1.1、 无锁可偏向,怎么理解?

最简单的说就是看markword布局百度一下一大把。101即偏向,准确的说thread指针按位或5(即二进制101)才是偏向,jvm的对象在初始时是01即无锁状态,启动时通过一个线程延迟调度将对象设置成101此时并没有java线程对对象进行加锁,何来偏向锁。最多只能说该对象是无锁,但是因为没上或者没上过锁,所以该对象是一个可以偏向某一线程的无锁对象。

//创建偏向的堆栈BiasedLocking::init biasedLocking.cpp:97Threads::create_vm thread.cpp:3663JNI_CreateJavaVM jni.cpp:5207InitializeJVM java.c:1214JavaMain java.c:376//设置偏向的线程堆栈enable_biased_locking biasedLocking.cpp:42Dictionary::classes_do dictionary.cpp:281SystemDictionary::classes_do systemDictionary.cpp:1764VM_EnableBiasedLocking::doit biasedLocking.cpp:57VM_Operation::evaluate vm_operations.cpp:62VMThread::evaluate_operation vmThread.cpp:377VMThread::loop vmThread.cpp:502VMThread::run vmThread.cpp:276java_start os_linux.cpp:828

看下源码

//biasedLocking.cpp
static void enable_biased_locking(Klass* k) {k->set_prototype_header(markOopDesc::biased_locking_prototype());
}

设置的内容:
可以看出只是设置了一个101
其他位并没有被使用。

1.2、偏向锁

来看看真正的偏向锁。为了方便将偏向延迟设置为0即-XX:BiasedLockingStartupDelay=0,依旧是上面那个demo,
下面是生成硬编码的源码,开始分析。在调用这个函数前lock_object函数中有一段  movptr(obj_reg, Address(lock_reg, obj_offset)); //mov    0x8(%rsi),%rcx意思的将lock record中的oop对象暂存到rcx寄存器中
void InterpreterMacroAssembler::lock_object(Register lock_reg) {............................................................................if (UseBiasedLocking) {biased_locking_enter(lock_reg, obj_reg, swap_reg, noreg, false, done, &slow_case);//这里已经解析过了}............................................................................
}int MacroAssembler::biased_locking_enter(Register lock_reg,                // rsiRegister obj_reg,                 // rcxRegister swap_reg,                    // raxRegister tmp_reg,                 // r10bool swap_reg_contains_mark,      // false rax不包括markword地址Label& done,                       // 跳转到锁结束的程序计数器处Label* slow_case,                   // 通过C 函数来上锁,当前及上面函数都是生成执行流汇编BiasedLockingCounters* counters) { LP64_ONLY( assert(tmp_reg != noreg, "tmp_reg must be supplied"); )bool need_tmp_reg = false;if (tmp_reg == noreg) {need_tmp_reg = true;tmp_reg = lock_reg;assert_different_registers(lock_reg, obj_reg, swap_reg);} else {assert_different_registers(lock_reg, obj_reg, swap_reg, tmp_reg);}Address mark_addr      (obj_reg, oopDesc::mark_offset_in_bytes());Address saved_mark_addr(lock_reg, 0);Label cas_label;int null_check_offset = -1;if (!swap_reg_contains_mark) {null_check_offset = offset();movptr(swap_reg, mark_addr);                       // swap_reg_contains_mark为false则将oop中的mark_word传入rax寄存器 mov    (%rcx),%rax}if (need_tmp_reg) {push(tmp_reg);}movptr(tmp_reg, swap_reg);                              // mov    %rax,%r10 偏向操作以r10为操作单元andptr(tmp_reg, markOopDesc::biased_lock_mask_in_place);// and    $0x7,%r10 只留后三位 cmpptr(tmp_reg, markOopDesc::biased_lock_pattern);      // and    $0x5,%r10 判断偏向锁位是否为1if (need_tmp_reg) {pop(tmp_reg);}jcc(Assembler::notEqual, cas_label);                      // 不可偏向直接跳转退出当前函数对应的执行流#ifndef _LP64movptr(saved_mark_addr, swap_reg);
#endifif (need_tmp_reg) {push(tmp_reg);}if (swap_reg_contains_mark) {null_check_offset = offset();}//  mov    0x8(%rcx),%r10d      获取klass或压缩后的klass指针//   shl    $0x3,%r10            如果是压缩指针,则还原。具体的请看https://blog.csdn.net/ckiuick/article/details/126655121我的元空间解析//    mov    0xb0(%r10),%r10      klass中的prototype_header,也就是klass的markOop  load_prototype_header(tmp_reg, obj_reg);
#ifdef _LP64orptr(tmp_reg, r15_thread);                             //or     %r15,%r10  设置偏向锁的偏向线程xorptr(tmp_reg, swap_reg);                                //xor    %rax,%r10  将对象oop中的markword和r10中的prototype_header的按位异或Register header_reg = tmp_reg;                          //header_reg 指向r10
#else
//..................................32位逻辑不管.................................
#endif
//and指令会影响zf位,假设执行上面三段代码时rax中已是偏向锁,并且是当前程,则or %r15,%r10执行后r10中的立即数和rax的立即数是相等,经过xor    %rax,%r10后,r10中的立即数后三位则为0,表示当前对象已经偏向当前线程了,and的时候zf为就变成1,jcc(Assembler::equal, done),jcc指令会跳转出去;而下方代码中的取反是为了排除分代年龄以免影响zf的结果andptr(header_reg, ~((int) markOopDesc::age_mask_in_place));
...............................................................................jcc(Assembler::equal, done);Label try_revoke_bias;Label try_rebias;
// 将header_reg和111相与,如果结果不为0,则表明header_reg后三位存在不为0的位,证明之前进行异或时,类的prototype_header后面三位与对象markOop的后三位不相等,但是能走到这,表明对象markword后三位为101,即偏向模式。现在类的prototype_header和对象markOop后三位不相等,即对象所属类不再支持偏向,发生了bulk_revoke,所以需要对当前对象进行偏向锁的撤销;否则表明目前该类还支持偏向锁,接着往下走。testptr(header_reg, markOopDesc::biased_lock_mask_in_place);jccb(Assembler::notZero, try_revoke_bias);// 测试对象所属类的prototype_header中epoch是否为0,不为0的话则表明之前异或时,类的prototype_header中epoch和对象markOop的epoch不相等,表明类在对象分配后发生过bulk_rebais()每次发生bulk_rebaise,类的prototype header中的epoch都会+1,所以之前对象的偏向就无效了,需要进行重偏向。否则接着往下走。testptr(header_reg, markOopDesc::epoch_mask_in_place);jccb(Assembler::notZero, try_rebias);NOT_LP64( movptr(swap_reg, saved_mark_addr); )andptr(swap_reg, //  取出对象markOop中除线程id之外的其他位markOopDesc::biased_lock_mask_in_place | markOopDesc::age_mask_in_place | markOopDesc::epoch_mask_in_place);if (need_tmp_reg) {push(tmp_reg);}
#ifdef _LP64movptr(tmp_reg, swap_reg); //将其他位和orptr(tmp_reg, r15_thread);//线程指针整合成一个markoop
#else
//..................................32位逻辑不管.................................
#endifif (os::is_MP()) {lock(); //汇编层面的lock}cmpxchgptr(tmp_reg, mark_addr); //cmpxchg %r10,(%rcx) rax和r10对比,相同设置到rcx中指针指向的内存,即oop中的markwordif (need_tmp_reg) {pop(tmp_reg);}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); // 如果cmpxchg未成功,需要走slow case}jmp(done);
//重偏向在上面“testptr(header_reg, markOopDesc::biased_lock_mask_in_place);”那段跳转bind(try_rebias);if (need_tmp_reg) {push(tmp_reg);}load_prototype_header(tmp_reg, obj_reg);
#ifdef _LP64orptr(tmp_reg, r15_thread);
#else
//..................................32位逻辑不管.................................
#endifif (os::is_MP()) {lock();}cmpxchgptr(tmp_reg, mark_addr); // compare tmp_reg and swap_regif (need_tmp_reg) {pop(tmp_reg);}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);//revoke在上面“testptr(header_reg, markOopDesc::epoch_mask_in_place);”那段跳转bind(try_revoke_bias);NOT_LP64( movptr(swap_reg, saved_mark_addr); )if (need_tmp_reg) {push(tmp_reg);}load_prototype_header(tmp_reg, obj_reg);if (os::is_MP()) {lock();}cmpxchgptr(tmp_reg, mark_addr); // compare tmp_reg and swap_regif (need_tmp_reg) {pop(tmp_reg);}if (counters != NULL) {cond_inc32(Assembler::zero,ExternalAddress((address) counters->revoked_lock_entry_count_addr()));}bind(cas_label);return null_check_offset;
}
//这两段中的逻辑和初次上偏向锁的基本一样不多述了

2、轻量|重量锁

为了方便先将偏向锁关闭

函数lock_object

//demo换一下
import java.util.concurrent.TimeUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;public class Test {public static Test syncTest = new Test();public static int  exp(int t ) throws InterruptedException {synchronized(syncTest /**Test.class**/) {int i = 1;TimeUnit.SECONDS.sleep(1000000000);}return 0;}public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {try {exp(1);} catch (InterruptedException e) {throw new RuntimeException(e);}});Thread t2 = new Thread(() -> {try {exp(2);} catch (InterruptedException e) {throw new RuntimeException(e);}});t1.start();t2.start();new Scanner(System.in).nextByte();}}//exp方法的字节码public static int exp(int) throws java.lang.InterruptedException;descriptor: (I)Iflags: ACC_PUBLIC, ACC_STATICCode:stack=3, locals=4, args_size=10: getstatic     #2                  // Field syncTest:LTest;3: dup4: astore_15: monitorenter6: iconst_17: istore_28: getstatic     #3                  // Field java/util/concurrent/TimeUnit.SECONDS:Ljava/util/concurrent/TimeUnit;11: ldc2_w        #4                  // long 1000000000l14: invokevirtual #6                  // Method java/util/concurrent/TimeUnit.sleep:(J)V17: aload_118: monitorexit19: goto          2722: astore_323: aload_124: monitorexit25: aload_326: athrow27: iconst_028: ireturnException table:from    to  target type6    19    22   any22    25    22   anyLineNumberTable:line 9: 0line 10: 6line 11: 8line 12: 17line 14: 27StackMapTable: number_of_entries = 2frame_type = 255 /* full_frame */offset_delta = 22locals = [ int, class java/lang/Object ]stack = [ class java/lang/Throwable ]frame_type = 250 /* chop */offset_delta = 4Exceptions:throws java.lang.InterruptedException
void InterpreterMacroAssembler::lock_object(Register lock_reg) {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 obj_reg  = 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;movptr(obj_reg, Address(lock_reg, obj_offset)); //mov    0x8(%rsi),%rcxif (UseBiasedLocking) {biased_locking_enter(lock_reg, obj_reg, swap_reg, noreg, false, done, &slow_case);}/** mov    $0x1,%eax    **/ movptr(swap_reg, (int32_t)1);//先加载一个无锁标识到rax/** or     (%rcx),%rax  **/ orptr(swap_reg, Address(obj_reg, 0));//和当前对象的对象的对象头 | 运算  01|01=1,01|00=01/**  mov    %rax,(%rsi)  **/ movptr(Address(lock_reg, mark_offset), swap_reg); //先无锁头载入lockrecordassert(lock_offset == 0, "displached header must be first word in BasicObjectLock");if (os::is_MP()) {lock();//LOCK前缀确}/**  rax无锁头和对象oop的markword对比 都是01则将rsi加载到rcx也就是oop中在前面monitorenter()函数中rsi寄存器存在的是基于当前os栈来开辟16字节大小的lockrecord monitorenter()函数lock record开辟代码段:mov    -0x40(%rbp),%rsi   __ movptr(c_rarg1, monitor_block_bot); // c_rarg1: old expression stack bottomsub    $0x10,%rsp       __ subptr(rsp, entry_size);            //rsp-0x10是开辟新的lockrecordsub    $0x10,%rsi         __ subptr(c_rarg1, entry_size);        //rsi-0x10是根据已有lockrecord的地址再次开辟一个**/                                                              /** cmpxchg %rsi,(%rcx) **/ cmpxchgptr(lock_reg, Address(obj_reg, 0)); if (PrintBiasedLockingStatistics) {                                      cond_inc32(Assembler::zero,ExternalAddress((address) BiasedLocking::fast_path_entry_count_addr()));}//markword无锁则跳出当前函数 无论是否跳转,markword中都已存在当前lockrecord对应的os栈地址。//如果当前是首个线程上的轻量级锁,到这里上锁流程解锁,跳出当前函数jcc(Assembler::zero, done);//这里开始抢锁逻辑,进这里表示要走slow_case//jvm原话就是通过((mark - rsp) & (7 - os::vm_page_size()))这个算式测试markword中是不是一个明显的栈指针。//如果已有线程堆该对象上锁,markword指向的是lockrecord也就是上一个线程栈的地址//这里还有一种情况不会走slow_case,验证java方法有没有递归。递归的栈是同一线程的栈。//当递归时,第一次进入这里,rsi寄存器已经设置了rbp-0x40-0x10的位置,并被设置到oop的markword中//7 - os::vm_page_size()是取底12位值为0x007二进制是0000 0000 0111//那么0000 0000 0111 到 1111 1111 1000之间有4089个字节加上7正好4096个字节,假设每个栈使用128个字节//则前31次递归可以使用当前and指令将zf设置为1(64位栈是8字节对齐,所以后三位一定是000),//可以让下面的jcc(Assembler::zero, done)成立跳过slow_casesubptr(swap_reg, rsp);   //递归举例:假设当前java方法需要栈大小为128,上次进入的栈地址为0x7FFFC497F440记录到markword,//二次(当次)进入时需要设置的便是0x7FFFC497F3C0,通过subptr(swap_reg, rsp)算出差值便是128//下面and就变成and 0xfffffffffffff007,80  0xfffffffffffff007的二进制是1111 ..中间48个1.. 0000 0000 0111//80的二进制是 0000 ..52个0.. 1000 0000 and后swap_reg变成0,zf置位1.jcc(Assembler::zero, done)成立跳转andptr(swap_reg, 7 - os::vm_page_size());//先保存到当前的lockrecord中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_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), lock_reg);//进入slow_case,InterpreterRuntime::monitorenter函数bind(done);}
}
lock_object小结:

这段主要使用了三个寄存器,rax:临时锁头,rsi:指向lockrecord,rcx:当前对象的oop。
1、先设置无锁头到rax并异或markword得到一个oop的是已经上锁的值
2、将rax和rcx中的markword对比,如果都是无锁则通过cmpxchg指令将rsi中栈指针放入rcx的oop中,跳出函数执行下一个字节码。如果cmpxchg失败,做执行slow_case前的准备
3、cmpxchg失败则需要抢锁,走slow_case,进入 InterpreterRuntime::monitorenter函数进行膨胀

函数 InterpreterRuntime::monitorenter

IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem))
.........................................................................................Handle h_obj(thread, elem->obj());if (UseBiasedLocking) {// Retry fast entry if bias is revoked to avoid unnecessary inflationObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK);} else {ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK);}
..........................................................................................
IRT_ENDvoid ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) {markOop mark = obj->mark();if (mark->is_neutral()) {//无锁状态进入lock->set_displaced_header(mark);if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) {//这节cas到markwordTEVENT (slow_enter: release stacklock) ;return ;}// Fall through to inflate() ...} elseif (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) {//有锁,且锁是当前线程的lock->set_displaced_header(NULL);return;}
#if 0
.................................................................................
#endif
//markOopDesc::unused_mark()值为3,大概意思从这里开始oop中的markword并不会设置到lockrecord中lock的markOop中
//所以是lockrecord中的标记并不重要,只要看上去不=像重入锁或已上锁就行lock->set_displaced_header(markOopDesc::unused_mark());ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);
}//锁膨胀
ObjectMonitor * ATTR ObjectSynchronizer::inflate (Thread * Self, oop object) {// Inflate mutates the heap ...// Relaxing assertion for bug 6320749.assert (Universe::verify_in_progress() ||!SafepointSynchronize::is_at_safepoint(), "invariant") ;for (;;) {const markOop mark = object->mark() ;assert (!mark->has_bias_pattern(), "invariant") ;if (mark->has_monitor()) { //markword最后两位是否为10重量锁ObjectMonitor * inf = mark->monitor() ;assert (inf->header()->is_neutral(), "invariant");assert (inf->object() == object, "invariant") ;assert (ObjectSynchronizer::verify_objmon_isinpool(inf), "monitor is invalid");return inf ;}if (mark == markOopDesc::INFLATING()) { //当前对象markword锁是否正在像重量锁膨胀TEVENT (Inflate: spin while INFLATING) ;ReadStableMark(object) ;continue ;}if (mark->has_locker()) { //当前对象markword按位& 3后是否为0即轻量级锁ObjectMonitor * m = omAlloc (Self) ; //申请Monitor对象m->Recycle();m->_Responsible  = NULL ;m->OwnerIsThread = 0 ;m->_recursions   = 0 ;m->_SpinDuration = ObjectMonitor::Knob_SpinLimit ;   // Consider: maintain by type/classmarkOop cmp = (markOop) Atomic::cmpxchg_ptr (markOopDesc::INFLATING(), object->mark_addr(), mark) ; //设置0,锁的膨胀中状态if (cmp != mark) {//或许是防止多线程cmpxchg跟换markwordomRelease (Self, m, true) ;continue ;       // Interference -- just retry}markOop dmw = mark->displaced_mark_helper() ;assert (dmw->is_neutral(), "invariant") ;// Setup monitor fields to proper values -- prepare the monitorm->set_header(dmw) ; //设置无锁头m->set_owner(mark->locker()); //设置当前线程栈(也就是属于哪个线程)m->set_object(object);//设置当前对象的oopguarantee (object->mark() == markOopDesc::INFLATING(), "invariant") ;//将当前的ObjectMonitor指正按位或2,设置到object(oop)的markword(重要)object->release_set_mark(markOopDesc::encode(m)); //....................................pref相关,省略......................................return m ;}void ATTR ObjectMonitor::enter(TRAPS) {Thread * const Self = THREAD ;void * cur ;//_owner:拥有锁的线程指针,如果当前对象已经是轻量锁则_owner是拥有锁的线程的线程栈地址即lockrecord对象//Self  :当前执行的线程即要竞争上锁的线程对象指针//cur   :cmpxchg设置成功则cur指向当前执行线程的指针,当前拥有锁的线程指针(或线程栈地址)//如果_owner属性是NULL就将其设置为Self,否则返回当前的_owner属性cur = Atomic::cmpxchg_ptr (Self, &_owner, NULL) ;if (cur == NULL) {return ;}if (cur == Self) {_recursions ++ ;//重入return ;}//轻量锁膨胀成重量级锁时,将owner设置为lock属性//在上面cmpxchg函数设置cur时cur指向了拥有锁的线程栈地址//Self->is_lock_owned函数是用当前线程的栈顶和栈底范围与cur对比,如果cur是在这一段栈范围内的//表示重入(嵌套java方法的中的syncchronized),将_recursions置为1后可直接返回if (Self->is_lock_owned ((address)cur)) {assert (_recursions == 0, "internal state error");_recursions = 1 ;// Commute owner from a thread-specific on-stack BasicLockObject address to// a full-fledged "Thread *"._owner = Self ;OwnerIsThread = 1 ;return ;}// We've encountered genuine contention.assert (Self->_Stalled == 0, "invariant") ;Self->_Stalled = intptr_t(this) ;if (Knob_SpinEarly && TrySpin (Self) > 0) { //Knob_SpinEarly默认为1,TrySpin 自旋assert (_owner == Self      , "invariant") ;assert (_recursions == 0    , "invariant") ;assert (((oop)(object()))->mark() == markOopDesc::encode(this), "invariant") ;Self->_Stalled = 0 ;return ;}
.............................................省略.........................................................for (;;) {jt->set_suspend_equivalent();EnterI (THREAD) ;if (!ExitSuspendEquivalent(jt)) break ;_recursions = 0 ;_succ = NULL ;exit (false, Self) ;jt->java_suspend_self();}Self->set_current_pending_monitor(NULL);}Atomic::dec_ptr(&_count);assert (_count >= 0, "invariant") ;Self->_Stalled = 0 ;
.............................................省略........................................................}void ATTR ObjectMonitor::EnterI (TRAPS) {Thread * Self = THREAD ;assert (Self->is_Java_thread(), "invariant") ;assert (((JavaThread *) Self)->thread_state() == _thread_blocked   , "invariant") ;// Try the lock - TATASif (TryLock (Self) > 0) {assert (_succ != Self              , "invariant") ;assert (_owner == Self             , "invariant") ;assert (_Responsible != Self       , "invariant") ;return ;}DeferredInitialize () ;//再次尝试自旋,获取锁成功则返回if (TrySpin (Self) > 0) {assert (_owner == Self        , "invariant") ;assert (_succ != Self         , "invariant") ;assert (_Responsible != Self  , "invariant") ;return ;}// The Spin failed -- Enqueue and park the thread ...assert (_succ  != Self            , "invariant") ;assert (_owner != Self            , "invariant") ;assert (_Responsible != Self      , "invariant") ;ObjectWaiter node(Self) ;Self->_ParkEvent->reset() ;node._prev   = (ObjectWaiter *) 0xBAD ;node.TState  = ObjectWaiter::TS_CXQ ;ObjectWaiter * nxt ;for (;;) {node._next = nxt = _cxq ;if (Atomic::cmpxchg_ptr (&node, &_cxq, nxt) == nxt) break ; //放入cxq队列头// Interference - the CAS failed because _cxq changed.  Just retry.// As an optional optimization we retry the lock.if (TryLock (Self) > 0) {assert (_succ != Self         , "invariant") ;assert (_owner == Self        , "invariant") ;assert (_Responsible != Self  , "invariant") ;return ;}}if ((SyncFlags & 16) == 0 && nxt == NULL && _EntryList == NULL) {//nxt和_EntryList为NULL,标识当前线程是第一个阻塞的线程,则将_Responsible设置为当前线程对象的指针Atomic::cmpxchg_ptr (Self, &_Responsible, NULL) ;}TEVENT (Inflated enter - Contention) ;int nWakeups = 0 ;int RecheckInterval = 1 ;int idx = 0;for (;;) {if (TryLock (Self) > 0) break ;assert (_owner != Self, "invariant") ;if ((SyncFlags & 2) && _Responsible == NULL) {//_Responsible将目标线程置为当前线程对象的指针Atomic::cmpxchg_ptr (Self, &_Responsible, NULL) ;}// park selfif (_Responsible == Self || (SyncFlags & 1)) {TEVENT (Inflated enter - park TIMED) ;Self->_ParkEvent->park ((jlong) RecheckInterval) ;//当前线程park// Increase the RecheckInterval, but clamp the value.RecheckInterval *= 8 ;if (RecheckInterval > 1000) RecheckInterval = 1000 ;} else {//当前线程不是第一个阻塞的线程,在上面一个循环中已经将当前线程加入cxq,这里可以直接park等待解锁唤醒TEVENT (Inflated enter - park UNTIMED) ;Self->_ParkEvent->park() ;}if (TryLock(Self) > 0) break ;TEVENT (Inflated enter - Futile wakeup) ;if (ObjectMonitor::_sync_FutileWakeups != NULL) {ObjectMonitor::_sync_FutileWakeups->inc() ;}++ nWakeups ;//Knob_SpinAfterFutile 默认1,最后的自旋获挣扎if ((Knob_SpinAfterFutile & 1) && TrySpin (Self) > 0) break ;//Knob_ResetEvent 默认0if ((Knob_ResetEvent & 1) && Self->_ParkEvent->fired()) {Self->_ParkEvent->reset() ;OrderAccess::fence() ;}if (_succ == Self) _succ = NULL ;tty->print_cr("lock cnt : %d " , idx++  );// Invariant: after clearing _succ a thread *must* retry _owner before parking.OrderAccess::fence() ;}assert (_owner == Self      , "invariant") ;assert (object() != NULL    , "invariant") ;UnlinkAfterAcquire (Self, &node) ;//这个函数写的比较难读,简单的说就是抢到锁后处理cxq、entrylist队列。//就是从cxq和entrylist中排除当前线程if (_succ == Self) _succ = NULL ;assert (_succ != Self, "invariant") ;if (_Responsible == Self) {_Responsible = NULL ;//将_Responsible置为NULL(重要)OrderAccess::fence(); // Dekker pivot-point}if (SyncFlags & 8) {OrderAccess::fence() ;}return ;
}
monitorenter小结:

轻量锁往重量锁膨胀,先通过inflate函数膨胀 object->release_set_mark(markOopDesc::encode(m)),即markword设置10。在通过enter函数判断是否重入,然后自旋。自旋失败,通过EnterI函数加入,并放入cxq队列头部,将java线程park。而当前的os线程无限循环与EnterI函数中最后一个for来TryLock。最后抢到锁则通过UnlinkAfterAcquire函数将当前线程从cxq或entrylist队列剔除。剔除后cxq或entrylist队指向当前node(ObjectMonitor)的next

三、锁消除 monitorexit

void TemplateTable::monitorexit() {transition(atos, vtos);/**    pop    %rax     cmp    (%rax),%rax**/__ 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 found;// find matching slot{Label entry, loop;/**   mov  -0x40(%rbp),%rsi   **/__ movptr(c_rarg1, monitor_block_top); //这两行都是从栈中/** lea  -0x40(%rbp),%rdx   **/__ lea(c_rarg2, monitor_block_bot);    //获得lockrecord// of monitor block             ┍ /**   jmp  bind(entry)        **/__ jmpb(entry);                  ┆                                                                                       ┆__ bind(loop);┆/** cmp  0x8(%rsi),%rax **/__ cmpptr(rax, Address(c_rarg1, BasicObjectLock::obj_offset_in_bytes()));┆/**    je    bind(found)   **/__ jcc(Assembler::equal, found);//   【lockrecord1】 -0x40(%rbp)┆// otherwise advance to next entry                           //   【lockrecord2】   -0x50(%rbp)┆/** add    $0x10,%rsi   **/__ addptr(c_rarg1, entry_size); //   【lockrecord3】   -0x60(%rbp)└/**跳转到这**/__ bind(entry);                             //假设一个java方法中有3个synchronize   // check if bottom reached                                //就会有3个lockrecord就线上面一样排列在栈中/**   cmp    %rdx,%rsi    **/__ cmpptr(c_rarg1, c_rarg2);   //因此这里在cmpptr(c_rarg1, c_rarg2)时,// if not at bottom then check this entry               //rsi指向lockrecord1,rdx指向lockrecord3/** jne bind(loop)  **/__ jcc(Assembler::notEqual, loop); //这样的话就需要循环找到当前对象}                                                              //在哪个lockrecord中// error handling. Unlocking was not block-structured/**  callq   InterpreterRuntime::throw_illegal_monitor_state_exception   **/__ call_VM(noreg, CAST_FROM_FN_PTR(address,InterpreterRuntime::throw_illegal_monitor_state_exception));__ should_not_reach_here();// call run-time routine// rsi: points to monitor entry__ bind(found);/**  push   %rax **/__ push_ptr(rax); // 对象oop压栈__ unlock_object(c_rarg1);/**    pop    %rax **/__ pop_ptr(rax); // 对象oop弹出到rax寄存器
}void InterpreterMacroAssembler::unlock_object(Register lock_reg) {assert(lock_reg == c_rarg1, "The argument is only for looks. It must be 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 instructionconst Register header_reg = c_rarg2;  // Will contain the old oopMarkconst Register obj_reg    = c_rarg3;  // Will contain the oop/**   mov  %r13,-0x38(%rbp) **/save_bcp(); // 保存当前bytecode point到栈中// Convert from BasicObjectLock structure to object and BasicLock// structure Store the BasicLock address into %rax/** lea (%rsi),%rax         **/ //栈中的lockrecord暂存到rax寄存器lea(swap_reg, Address(lock_reg, BasicObjectLock::lock_offset_in_bytes()));/**   mov 0x8(%rsi),%rcx      **/ //lockrecord中的oop指针存入rcx寄存器movptr(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset_in_bytes()));/** movq   $0x0,0x8(%rsi)     **/ //lockrecord的oop指针置空movptr(Address(lock_reg, BasicObjectLock::obj_offset_in_bytes()), (int32_t)NULL_WORD);if (UseBiasedLocking) {biased_locking_exit(obj_reg, header_reg, done);}/**    mov    (%rax),%rdx  **/     //lockrecord中lock的markOop存入rdxmovptr(header_reg, Address(swap_reg,BasicLock::displaced_header_offset_in_bytes()));// Test for recursion         //对markOop判空/** test   %rdx,%rdx    **/testptr(header_reg, header_reg);// zero for recursive case       //空表示无锁直接跳出/**  je     bind(done)   **/jcc(Assembler::zero, done);// Atomic swap back the old headerif (os::is_MP()) lock(); /** lock **/// rcx存着对象oop,将oop中的markword,通过rax和rdx对比,相同则设置到rdx中并设置zf位为1/**    cmpxchg %rdx,(%rcx) **/cmpxchgptr(header_reg, Address(obj_reg, 0));// zero for recursive case/**    je     bind(done)   **/jcc(Assembler::zero, done);/**   mov    %rcx,0x8(%rsi)   **/movptr(Address(lock_reg, BasicObjectLock::obj_offset_in_bytes()),obj_reg); // restore obj/** callq InterpreterRuntime::monitorexit   **/ //执行InterpreterRuntime::monitorexitcall_VM(noreg,CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit),lock_reg);bind(done);/**    mov    -0x38(%rbp),%r13 **/restore_bcp();}
}IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorexit(JavaThread* thread, BasicObjectLock* elem))
#ifdef ASSERTthread->last_frame().interpreter_frame_verify_monitor(elem);
#endifHandle h_obj(thread, elem->obj());
.............................................................ObjectSynchronizer::slow_exit(h_obj(), elem->lock(), thread);
.............................................................
IRT_ENDvoid ObjectSynchronizer::slow_exit(oop object, BasicLock* lock, TRAPS) {fast_exit (object, lock, THREAD) ;
}void ObjectSynchronizer::fast_exit(oop object, BasicLock* lock, TRAPS) {assert(!object->mark()->has_bias_pattern(), "should not see bias pattern here");// if displaced header is null, the previous enter is recursive enter, no-opmarkOop dhw = lock->displaced_header();//获取lockrecord中lock的markOopmarkOop mark ;if (dhw == NULL) { //能进来这个函数一般不会是null// Recursive stack-lock.// Diagnostics -- Could be: stack-locked, inflating, inflated.mark = object->mark() ;assert (!mark->is_neutral(), "invariant") ;if (mark->has_locker() && mark != markOopDesc::INFLATING()) {assert(THREAD->is_lock_owned((address)mark->locker()), "invariant") ;}if (mark->has_monitor()) {ObjectMonitor * m = mark->monitor() ;assert(((oop)(m->object()))->mark() == mark, "invariant") ;assert(m->is_entered(THREAD), "invariant") ;}return ;}mark = object->mark() ;//获取对象oop的markword(不出意外应该是ObjectMonitor*指正按位或2)// lock中存不出意外应该是monitorenter时的栈地址if (mark == (markOop) lock) {assert (dhw->is_neutral(), "invariant") ;if ((markOop) Atomic::cmpxchg_ptr (dhw, object->mark_addr(), mark) == mark) {TEVENT (fast_exit: release stacklock) ;return;}}ObjectSynchronizer::inflate(THREAD, object)->exit (true, THREAD) ;
}ObjectMonitor * ATTR ObjectSynchronizer::inflate (Thread * Self, oop object) {//...................................................................for (;;) {//此时应该是存在ObjectMonitor ,所以只要获得ObjectMonitor指针就可以返回了if (mark->has_monitor()) {ObjectMonitor * inf = mark->monitor() ;assert (inf->header()->is_neutral(), "invariant");assert (inf->object() == object, "invariant") ;assert (ObjectSynchronizer::verify_objmon_isinpool(inf), "monitor is invalid");return inf ;}}//..........................................................................................
}void ATTR ObjectMonitor::exit(bool not_suspended, TRAPS) {Thread * Self = THREAD ;if (THREAD != _owner) {//需要解锁的线程是否为当前线程if (THREAD->is_lock_owned((address) _owner)) {assert (_recursions == 0, "invariant") ;_owner = THREAD ;_recursions = 0 ;OwnerIsThread = 1 ;} else {// NOTE: we need to handle unbalanced monitor enter/exit// in native code by throwing an exception.// TODO: Throw an IllegalMonitorStateException ?TEVENT (Exit - Throw IMSX) ;assert(false, "Non-balanced monitor enter/exit!");if (false) {THROW(vmSymbols::java_lang_IllegalMonitorStateException());}return;}}if (_recursions != 0) {//如果是重入锁_recursions--;        // this is simple recursive enterTEVENT (Inflated exit - recursive) ;return ;}// SyncFlags默认是0,_Responsible置空if ((SyncFlags & 4) == 0) {_Responsible = NULL ;}#if INCLUDE_TRACE// get the owner's thread id for the MonitorEnter event// if it is enabled and the thread isn't suspendedif (not_suspended && Tracing::is_event_enabled(TraceJavaMonitorEnterEvent)) {_previous_owner_tid = SharedRuntime::get_java_tid(Self);}
#endiffor (;;) {assert (THREAD == _owner, "invariant") ;
//Knob_ExitPolicy 默认0if (Knob_ExitPolicy == 0) {OrderAccess::release_store_ptr (&_owner, NULL) ;   // drop the lockOrderAccess::storeload() ;                         // See if we need to wake a successor//没有线程需要获取锁 直接返回if ((intptr_t(_EntryList)|intptr_t(_cxq)) == 0 || _succ != NULL) {TEVENT (Inflated exit - simple egress) ;return ;}TEVENT (Inflated exit - complex egress) ;//在下面代码中通过ExitEpilog函数来解锁的_owner都被置空了,这里直接returnif (Atomic::cmpxchg_ptr (THREAD, &_owner, NULL) != NULL) {return ;}TEVENT (Exit - Reacquired) ;} else {if ((intptr_t(_EntryList)|intptr_t(_cxq)) == 0 || _succ != NULL) {OrderAccess::release_store_ptr (&_owner, NULL) ;   // drop the lockOrderAccess::storeload() ;// Ratify the previously observed values.if (_cxq == NULL || _succ != NULL) {TEVENT (Inflated exit - simple egress) ;return ;}if (Atomic::cmpxchg_ptr (THREAD, &_owner, NULL) != NULL) {TEVENT (Inflated exit - reacquired succeeded) ;return ;}TEVENT (Inflated exit - reacquired failed) ;} else {TEVENT (Inflated exit - complex egress) ;}}guarantee (_owner == THREAD, "invariant") ;ObjectWaiter * w = NULL ;//Knob_QMode 默认为0 //QMode:抢锁者队列的唤醒顺序用哪个模式int QMode = Knob_QMode ;if (QMode == 2 && _cxq != NULL) { //从cxq队列唤醒w = _cxq ;//cxq队列头节点assert (w != NULL, "invariant") ;assert (w->TState == ObjectWaiter::TS_CXQ, "Invariant") ;ExitEpilog (Self, w) ;//将w这个节点中的owner置空已达到解锁的目的 并唤醒其他java线程来抢锁return ;}if (QMode == 3 && _cxq != NULL) {w = _cxq ;for (;;) {assert (w != NULL, "Invariant") ;ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL, &_cxq, w) ;if (u == w) break ;w = u ;}assert (w != NULL              , "invariant") ;ObjectWaiter * q = NULL ;ObjectWaiter * p ;for (p = w ; p != NULL ; p = p->_next) {//设置TState为TS_ENTER ,在MonitorEnter抢到锁时有使用guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ;p->TState = ObjectWaiter::TS_ENTER ;p->_prev = q ;q = p ;}ObjectWaiter * Tail ;for (Tail = _EntryList ; Tail != NULL && Tail->_next != NULL ; Tail = Tail->_next) ;if (Tail == NULL) {_EntryList = w ;} else {//QMode=3 如果entrylist不为空,则将cxq链接entrylist尾部 entrylist = entrylist->cxq,Tail->_next = w ;w->_prev = Tail ;}}if (QMode == 4 && _cxq != NULL) {w = _cxq ;for (;;) {assert (w != NULL, "Invariant") ;ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL, &_cxq, w) ;if (u == w) break ;w = u ;}assert (w != NULL              , "invariant") ;ObjectWaiter * q = NULL ;ObjectWaiter * p ;for (p = w ; p != NULL ; p = p->_next) {guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ;p->TState = ObjectWaiter::TS_ENTER ;p->_prev = q ;q = p ;}//QMode=4 将cxq链接到entryList头部 entrylist = cxq->entrylistif (_EntryList != NULL) {q->_next = _EntryList ;_EntryList->_prev = q ;}_EntryList = w ;}//QMode默认情况下从这里开始w = _EntryList  ;//看情况,如果多个线程竞争,这里就不为null//具体看UnlinkAfterAcquire函数的if (SelfNode->TState == ObjectWaiter::TS_ENTER)判断这段if (w != NULL) {assert (w->TState == ObjectWaiter::TS_ENTER, "invariant") ;ExitEpilog (Self, w) ;//将w这个节点中的owner置空已达到解锁的目的 并唤醒其他java线程来抢锁return ;}//走这里表示不存在_EntryList队列的竞争者,//例如:在一个线程解锁时将要解锁线程在队列中node的Tstate设置为TS_ENTER,//在这个间隙时间正好有一个不存在队列中的线程来抢锁并抢到锁,//此时该线程加入cxq时的node的Tstate是TS_CXQ,这样就即便上锁成功也不会被放入_EntryList队列//那么走到这里表示_EntryList  中的竞争全解锁完了在轮到这w = _cxq ;if (w == NULL) continue ;for (;;) {assert (w != NULL, "Invariant") ;ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL, &_cxq, w) ;//_cxq头结点置空if (u == w) break ;w = u ;}TEVENT (Inflated exit - drain cxq into EntryList) ;assert (w != NULL              , "invariant") ;assert (_EntryList  == NULL    , "invariant") ;if (QMode == 1) {// QMode == 1 : drain cxq to EntryList, reversing order// We also reverse the order of the list.ObjectWaiter * s = NULL ;ObjectWaiter * t = w ;ObjectWaiter * u = NULL ;while (t != NULL) {//队列转置,QMode =1 :1234变成4321guarantee (t->TState == ObjectWaiter::TS_CXQ, "invariant") ;t->TState = ObjectWaiter::TS_ENTER ;u = t->_next ;t->_prev = u ;t->_next = s ;s = t;t = u ;}_EntryList  = s ;assert (s != NULL, "invariant") ;} else {// QMode == 0 or QMode == 2 队列顺序不变,让_EntryList 指向cxq,// 并每个node的TState 设置为TS_ENTER _EntryList = w ;ObjectWaiter * q = NULL ;ObjectWaiter * p ;for (p = w ; p != NULL ; p = p->_next) {guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ;p->TState = ObjectWaiter::TS_ENTER ;p->_prev = q ;q = p ;}}if (_succ != NULL) continue;w = _EntryList  ;if (w != NULL) {guarantee (w->TState == ObjectWaiter::TS_ENTER, "invariant") ;ExitEpilog (Self, w) ;//将w这个节点中的owner置空已达到解锁的目的 并唤醒其他java线程来抢锁return ;}}
}

总结

上锁:

无论是偏向锁还是轻量、重量锁都需要从os堆栈的rbp - 0x40到(rbp - 0x40)-0x10位置开辟16字节的空间用作lock record。lock record即BasicObjectLock,该类中存在两个变量 BasicLock _lock; 和oop _obj; _lock变量用于存储锁相关的内容。
TemplateTable::monitorenter()中movptr(Address(c_rarg1, BasicObjectLock::obj_offset_in_bytes()), rax)将oop设置BasicObjectLock的oop变量中

偏向锁:

通过lock_object->biased_locking_enter函数中orptr(tmp_reg, r15_thread)设置到BasicObjectLock的BasicLock _lock然后通过cmpxchgptr(tmp_reg, mark_addr)设置到oop的markword中

轻量锁 | 重量锁:

寄存器对应关系:
swap_reg = rax,
oop = obj_reg = rcx,
lock record = lock_reg = rsi。

在lock_object函数中movl(swap_reg, 1)生成一个临时锁头放入无锁标识,orptr(swap_reg, Address(obj_reg, 0))临时锁头与当前oop中的markWord做异或,如果是无锁则001 | 1 = 1、已是轻量锁00 | 1 = 00、已是重量锁010 | = 11 设置到临时锁头中,轻量锁和重量锁下其他61位则是线程栈地址。下一步movptr(Address(lock_reg, mark_offset), swap_reg)将临时锁头设置到lock record的 BasicLock _lock变量中,
此时 lock_reg 指向线程栈地址,lock_reg 中的_lock在有锁的情况存着线程栈地址 | 1,无锁则是1。obj_reg中的首地址有锁则是上一个上锁线程的线程栈lock record,无锁则是1。swap_reg则根据obj_reg情况来确定。
通过cmpxchgptr(lock_reg, Address(obj_reg, 0))cmpxchg

轻量锁:

在无锁->轻量锁下,obj_reg的首地址指针指向的内存:markWord和swap_reg 中临时无锁标志位都是01无锁,cmpxchg对比先等,将lock_reg设置到markWord并设置ZF位为1。最后jcc(Assembler::zero, done),此时ZF位=1,则跳转到一下字节码。

重量锁:

cmpxchg 对比不相等表示oop中markword有存在线程地址或线程栈地址,markword也会通过cmpxchg 设置到swap_reg ,然后走slow_case,进入ObjectSynchronizer::slow_enter函数。
ObjectSynchronizer::slow_enter函数中会先判断oop的markword是否包含了ObjectMonitor* | 10的值,linux中jdk1.8一个对象oop如果上过重量锁,markword中的这个值是不会回收(windows会)。
通过调用ObjectSynchronizer::inflate(THREAD, obj())函数封装一个ObjectMonitor * m,如果markword已存在ObjectMonitor* | 10的值,则用markword^10来获得ObjectMonitor*。如果不存在将锁标志位膨胀成10即重量锁标志放入ObjectMonitor 中, 在将这个ObjectMonitor指针与10做按位与运算,到oop的markword中返回,并设置一个 设置_owner当前线程指针,后返回。继而调用ObjectMonitor::enter(TRAPS)函数,该函数中会通过

//判断当前ObjectMonitor是否被一个线程拥有
//如果是已经过上锁并解锁的对象_owner线程指针会在解锁过程中置空,cur =null
//如果是膨胀对象先会在inflate函数设置_owner线程指针cur = _owner
cur = Atomic::cmpxchg_ptr (Self, &_owner, NULL) ;if (cur == NULL) {// Either ASSERT _recursions == 0 or explicitly set _recursions = 0.assert (_recursions == 0   , "invariant") ;assert (_owner      == Self, "invariant") ;// CONSIDER: set or assert OwnerIsThread == 1return ;}

然后进行现自旋以及抢锁,大概流程是:
自旋(TrySpin)->抢锁(TryLock)->自旋->进入cxq队列头->抢锁->死循环(抢锁->抢锁->自旋)获得锁后进入ObjectMonitor::UnlinkAfterAcquire (Thread * Self, ObjectWaiter * SelfNode)函数来处理队列
抢锁的线程会被ObjectWaiter node(Self)封装后才会进入cxq队列,ObjectMonitor::UnlinkAfterAcquire函数中会判断当前node的TState,TState初始时是ObjectWaiter::TS_CXQ。当一个线程解锁时会将队列中所有竞争者的TState设置为ObjectWaiter::TS_ENTER。在函数中如果是TS_CXQ将当前node从cxq中剔除,如果是TS_ENTER除了node剔除还会设置EntryList指向当前node的next节点。

解锁:

增加一个寄存器对应关系:header_reg = rdx
1、TemplateTable::monitorexit()函数中获得栈中lock record
2、在TemplateTable::unlock_object中将lock record中的锁头BasicLock _lock设置到swap_reg,oop _obj设置到obj_reg,swap_reg指向的内容即无锁标志设置到header_reg中
3、lock record中的oop指针置空(置0)
4、通过cmpxchg,对比swap_reg和obj_reg。
4.1、 相等:轻量锁 将header_reg无锁标志设置到对象oop的marword中跳出解锁
4.2、不相等(默认重量锁不考虑其他情况):准备进入InterpreterRuntime::monitorexit函数,调用顺序
InterpreterRuntime::monitorexit->ObjectSynchronizer::slow_exit->ObjectSynchronizer::fast_exit
ObjectSynchronizer::fast_exit函数中先调用ObjectSynchronizer::inflate,该函数通过oop的markword中的值和monitor_value取非来获得ObjectMonitor指针(ObjectMonitor*) (value() ^ monitor_value)然后调用ObjectMonitor::exit。最后调用ExitEpilog删除节点中_owner(拥有锁的线程)
不考虑重入,QMode=0的情况下,先将_EntryList的头结点拿出不为空(不为空原因结合结合抢锁成功后调用ObjectMonitor::UnlinkAfterAcquire看)则调用ObjectMonitor::ExitEpilog函数将这个节点的_owner线程指针置空(这里和上面的抢锁呼应上了)。_EntryList为空的话就从cxq中取头结点,将_EntryList指向cxq并将所有节点的TStat设置为ObjectWaiter::TS_ENTER,在将_EntryList的头结点拿出调用ObjectMonitor::ExitEpilog_owner置空线程指针

synchronized源码解析相关推荐

  1. synchronized 和 reentrantlock 区别是什么_JUC源码系列之ReentrantLock源码解析

    目录 ReentrantLock 简介 ReentrantLock 使用示例 ReentrantLock 与 synchronized 的区别 ReentrantLock 实现原理 Reentrant ...

  2. 彻底理解OkHttp - OkHttp 源码解析及OkHttp的设计思想

    OkHttp 现在统治了Android的网络请求领域,最常用的框架是:Retrofit+okhttp.OkHttp的实现原理和设计思想是必须要了解的,读懂和理解流行的框架也是程序员进阶的必经之路,代码 ...

  3. Framework 源码解析知识梳理(5) startService 源码分析

    一.前言 最近在看关于插件化的知识,遇到了如何实现Service插件化的问题,因此,先学习一下Service内部的实现原理,这里面会涉及到应用进程和ActivityManagerService的通信, ...

  4. HandlerThread和IntentService源码解析

    简介 首先我们先来了解HandlerThread和IntentService是什么,以及为什么要将这两者放在一起分析. HandlerThread: HandlerThread 其实是Handler ...

  5. EventBus源码解析

    前面一篇文章讲解了EventBus的使用,但是作为开发人员,不能只停留在仅仅会用的层面上,我们还需要弄清楚它的内部实现原理.所以本篇博文将分析EventBus的源码,看看究竟它是如何实现"发 ...

  6. spring aop 注入源码解析

    spring aop 注入源码解析 aop启动 AbstractApplicationContext.java @Overridepublic void refresh() throws BeansE ...

  7. Volley 源码解析之图片请求

    一.前言 上篇文章我们分析了网络请求,这篇文章分析对图片的处理操作,如果没看上一篇,可以先看上一篇文章Volley 源码解析之网络请求.Volley 不仅仅对请求网络数据作了良好的封装,还封装了对图片 ...

  8. 死磕 java同步系列之ReentrantReadWriteLock源码解析

    问题 (1)读写锁是什么? (2)读写锁具有哪些特性? (3)ReentrantReadWriteLock是怎么实现读写锁的? (4)如何使用ReentrantReadWriteLock实现高效安全的 ...

  9. Handler消息机制(九):IntentService源码解析

    作者:jtsky 链接:https://www.jianshu.com/p/0a150ec09a32 简介 首先我们先来了解HandlerThread和IntentService是什么,以及为什么要将 ...

最新文章

  1. RxJava firstElement 与 lastElement 以及 elementAt
  2. 利用Use Case为系统行为建模(3)
  3. 运维开发必会技能之一——虚拟机管理
  4. python set没有顺序_Python一题多解学思路:指定列前置
  5. 关于flex布局的深入学习
  6. 解决问题的能力 10倍程序员
  7. PyTorch是个啥玩意儿?
  8. openssh8.6升级修复(CVE-2020-15778)(CVE-2018-15919)(CVE-2017-15906)等漏洞
  9. 基于Teigha.Net实现CAD到SHP的转换方案
  10. 【Elasticsearch】Elasticsearch的数据类型 (text、keyword、date、object、geo等)
  11. 企业微信发送应用消息的实现
  12. 浪涌保护器ant120_ANT120/530/1P浪涌保护器服务周到漳州
  13. 人工智能产品经理如何面对数据挖掘
  14. Python 计算两点之间的距离
  15. python一阶差分_Python使用pandas对数据进行差分运算的方法
  16. R语言——矩阵中删除缺省值可用的函数
  17. 骨传导耳机是利用什么原理听歌?什么骨传导耳机好用
  18. 停止抱怨英语_停止抱怨
  19. matlab实验报告井字棋,有偿井字棋游戏300+
  20. 优化策略5 Label Smoothing Regularization_LSR原理分析

热门文章

  1. 职中计算机一级证,职中计算机等级一级考证教学网站的设计
  2. Qt geometry
  3. 商用在线客服软件测试报告
  4. lucas求大组合数
  5. 如何推导出Gamma分布
  6. Linux 文件系统的工作原理深度透析
  7. 山贼集团 (group)
  8. 青海平弦乐库的建设与播放平台
  9. 华为的鸿蒙系统是海思_华为鸿蒙系统能成为超算系统吗?华为硬件可以组成生态圈,可行!...
  10. hexo yilia主题添加评论系统详细教程