synchronized源码解析

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

我的环境 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.class
  Last modified 2022-12-17; size 394 bytes
  MD5 checksum ed0edf2dd2f862dbd54dd1820b813ed4
  Compiled from "Test.java"
public class Test
  minor version: 0
  major version: 52
  flags: 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: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 2: 0

  public static synchronized int exp(int) throws java.lang.InterruptedException;
    descriptor: (I)I
    flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
    Code:
      stack=1, locals=1, args_size=1
         0: iconst_0
         1: ireturn
      LineNumberTable:
        line 5: 0
    Exceptions:
      throws java.lang.InterruptedException

  public static void main(java.lang.String[]) throws java.lang.InterruptedException;
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=1, args_size=1
         0: iconst_1
         1: invokestatic  #2                  // Method exp:(I)I
         4: pop
         5: return
      LineNumberTable:
        line 9: 0
        line 10: 5
    Exceptions:
      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 method
  const 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 aligned
  oop       _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 instruction
    const Register obj_reg  = rcx;  // Will contain the oop

    const int obj_offset = BasicObjectLock::obj_offset_in_bytes();
    const int lock_offset = BasicObjectLock::lock_offset_in_bytes ();
    const int mark_offset = lock_offset + BasicLock::displaced_header_offset_in_bytes();

    Label slow_case;

    movptr(obj_reg, Address(lock_reg, obj_offset)); //mov    0x8(%rsi),%rcx

    if (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:97
    Threads::create_vm thread.cpp:3663
    JNI_CreateJavaVM jni.cpp:5207
    InitializeJVM java.c:1214
    JavaMain java.c:376

//设置偏向的线程堆栈
	enable_biased_locking biasedLocking.cpp:42
	Dictionary::classes_do dictionary.cpp:281
	SystemDictionary::classes_do systemDictionary.cpp:1764
	VM_EnableBiasedLocking::doit biasedLocking.cpp:57
	VM_Operation::evaluate vm_operations.cpp:62
	VMThread::evaluate_operation vmThread.cpp:377
	VMThread::loop vmThread.cpp:502
	VMThread::run vmThread.cpp:276
	java_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, 				// rsi
                                         Register obj_reg,					// rcx
                                         Register swap_reg,					// rax
                                         Register tmp_reg,					// r10
                                         bool 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 判断偏向锁位是否为1
  if (need_tmp_reg) {
    pop(tmp_reg);
  }
  jcc(Assembler::notEqual, cas_label);					  // 不可偏向直接跳转退出当前函数对应的执行流

#ifndef _LP64

  movptr(saved_mark_addr, swap_reg);
#endif
  if (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 _LP64
  orptr(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 _LP64
  movptr(tmp_reg, swap_reg); //将其他位和
  orptr(tmp_reg, r15_thread);//线程指针整合成一个markoop
#else
//..................................32位逻辑不管.................................
#endif
  if (os::is_MP()) {
    lock(); //汇编层面的lock
  }
  cmpxchgptr(tmp_reg, mark_addr); //cmpxchg %r10,(%rcx) rax和r10对比,相同设置到rcx中指针指向的内存,即oop中的markword
  if (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 _LP64
  orptr(tmp_reg, r15_thread);
#else
//..................................32位逻辑不管.................................
#endif
  if (os::is_MP()) {
    lock();
  }
  cmpxchgptr(tmp_reg, mark_addr); // compare tmp_reg and swap_reg
  if (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_reg
  if (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)I
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=4, args_size=1
         0: getstatic     #2                  // Field syncTest:LTest;
         3: dup
         4: astore_1
         5: monitorenter
         6: iconst_1
         7: istore_2
         8: getstatic     #3                  // Field java/util/concurrent/TimeUnit.SECONDS:Ljava/util/concurrent/TimeUnit;
        11: ldc2_w        #4                  // long 1000000000l
        14: invokevirtual #6                  // Method java/util/concurrent/TimeUnit.sleep:(J)V
        17: aload_1
        18: monitorexit
        19: goto          27
        22: astore_3
        23: aload_1
        24: monitorexit
        25: aload_3
        26: athrow
        27: iconst_0
        28: ireturn
      Exception table:
         from    to  target type
             6    19    22   any
            22    25    22   any
      LineNumberTable:
        line 9: 0
        line 10: 6
        line 11: 8
        line 12: 17
        line 14: 27
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 22
          locals = [ int, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 4
    Exceptions:
      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 instruction
    const Register obj_reg  = rcx;  // Will contain the oop

    const int obj_offset = BasicObjectLock::obj_offset_in_bytes();
    const int lock_offset = BasicObjectLock::lock_offset_in_bytes ();
    const int mark_offset = lock_offset + BasicLock::displaced_header_offset_in_bytes();

    Label slow_case;

    movptr(obj_reg, Address(lock_reg, obj_offset)); //mov    0x8(%rsi),%rcx

    if (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); //先无锁头载入lockrecord

    assert(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 bottom
      sub    $0x10,%rsp     	__ subptr(rsp, entry_size);            //rsp-0x10是开辟新的lockrecord
      sub    $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_case
    subptr(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 inflation
    ObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK);
  } else {
    ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK);
  }
..........................................................................................
IRT_END


void 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到markword
      TEVENT (slow_enter: release stacklock) ;
      return ;
    }
    // Fall through to inflate() ...
  } else
  if (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/class

          markOop cmp = (markOop) Atomic::cmpxchg_ptr (markOopDesc::INFLATING(), object->mark_addr(), mark) ; //设置0,锁的膨胀中状态
          if (cmp != mark) {//或许是防止多线程cmpxchg跟换markword
             omRelease (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 monitor
          m->set_header(dmw) ; //设置无锁头

          m->set_owner(mark->locker()); //设置当前线程栈(也就是属于哪个线程)
          m->set_object(object);//设置当前对象的oop
        
          guarantee (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 - TATAS
    if (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 self
        if (_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 默认0
        if ((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 instruction
    const Register header_reg = c_rarg2;  // Will contain the old oopMark
    const 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存入rdx
    movptr(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 header
    if (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::monitorexit
    call_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 ASSERT
  thread->last_frame().interpreter_frame_verify_monitor(elem);
#endif
  Handle h_obj(thread, elem->obj());
.............................................................
  ObjectSynchronizer::slow_exit(h_obj(), elem->lock(), thread);
.............................................................
IRT_END

void 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-op
  markOop dhw = lock->displaced_header();//获取lockrecord中lock的markOop
  markOop 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 enter
     TEVENT (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 suspended
   if (not_suspended && Tracing::is_event_enabled(TraceJavaMonitorEnterEvent)) {
     _previous_owner_tid = SharedRuntime::get_java_tid(Self);
   }
#endif

   for (;;) {
      assert (THREAD == _owner, "invariant") ;
//Knob_ExitPolicy 默认0
      if (Knob_ExitPolicy == 0) {
         OrderAccess::release_store_ptr (&_owner, NULL) ;   // drop the lock
         OrderAccess::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都被置空了,这里直接return
         if (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 lock
            OrderAccess::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->entrylist
          if (_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变成4321
             guarantee (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 == 1
    return ;
 }

然后进行现自旋以及抢锁,大概流程是:
自旋(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置空线程指针

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值