monsterhxw / my-notes

技术随想
MIT License
2 stars 0 forks source link

Java synchronized 的 Biased Lock (偏向锁) 源码分析 #1

Open monsterhxw opened 10 months ago

monsterhxw commented 10 months ago

synchronized 分析

获取偏向锁

CASE(_monitorenter)::bytecodeInterpreter
  |-lockee = STACK_OBJECT(-1) // 获取锁对象
  |-if (entry != NULL) // 找到空闲的 BasicObjectLock (即 Lock Record)
  |   |-entry->set_obj(lockee) // BasicObjectLock->_obj 设置锁对象
  |   |-success = false
  |   |-mark = lockee->mark() // 获取锁对象 markword
  |   |-if (mark->has_bias_pattern()) // 锁对象 mark word 是否有偏向锁标志位,即 101
  |   |   |-if  (anticipated_bias_locking_value == 0) // markword 的线程 ID 就是当前线程而且「锁对象」的 markword 的 epoch 等于「锁对象的类」的 epoch
  |   |   |-else if ((anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) != 0)  // 如果偏向模式关闭,则「撤销」偏向锁
  |   |   |-else if ((anticipated_bias_locking_value & epoch_mask_in_place) !=0) // 如果 epoch 已过期,则需要重偏向
  |   |   |-else { // 1. 要么匿名偏向 2. 要么「已经偏向别的线程」
  |   |   |   | // CAS 操作尝试以「匿名偏向 (即线程 ID 为 0) 的 markword」作为比较值,以「偏向当前线程的 markword」作为替换值,设置「锁对象的 markword」值
  |   |   |   |-if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), header) == header)
  |   |   |   |-else // 失败,证明存在多线程竞争,,则调用 monitorenter 进行「偏向撤销 (由 VM 线程撤销)」

BasicObjectLock 和 BasicLock 定义

源码: https://github.com/openjdk/jdk8u/blob/master/hotspot/src/share/vm/runtime/basicLock.hpp#L32-L78

// BasicObjectLock
class BasicObjectLock VALUE_OBJ_CLASS_SPEC {
  friend class VMStructs;
 private:
  // BasicLock 保存 Lock Record 即保存「锁对象」的 markword
  BasicLock _lock;                                    // the lock, must be double word aligned
  // 锁对象
  oop       _obj;                                     // object holds the lock;

 public:
  // Manipulation
  oop      obj() const                                { return _obj;  }
  void set_obj(oop obj)                               { _obj = obj; }
  BasicLock* lock()                                   { return &_lock; }
};

// BasicLock
class BasicLock VALUE_OBJ_CLASS_SPEC {
  friend class VMStructs;
 private:
  // 这里就是保存「锁对象」的 markword
  volatile markOop _displaced_header;
 public:
  markOop      displaced_header() const               { return _displaced_header; }
  void         set_displaced_header(markOop header)   { _displaced_header = header; }
};

BytecodeInterpreter 定义

源码: https://github.com/openjdk/jdk8u/blob/master/hotspot/src/share/vm/interpreter/bytecodeInterpreter.hpp#L94-L612

class BytecodeInterpreter : StackObj {
private:
    JavaThread*           _thread;        // the vm's java thread pointer
    address               _bcp;           // instruction pointer
    intptr_t*             _locals;        // local variable pointer
    ConstantPoolCache*    _constants;     // constant pool cache
    Method*               _method;        // method being executed
    DataLayout*           _mdx;           // compiler profiling data for current bytecode
    intptr_t*             _stack;         // expression stack
    messages              _msg;           // frame manager <-> interpreter message
    frame_manager_message _result;        // result to frame manager
    interpreterState      _prev_link;     // previous interpreter state
    oop                   _oop_temp;      // mirror for interpreted native, null otherwise
    intptr_t*             _stack_base;    // base of expression stack
    intptr_t*             _stack_limit;   // limit of expression stack
    // 管程 monitor 的栈底 BasicObjectLock (即 Lock Record)
    BasicObjectLock*      _monitor_base;  // base of monitors on the native stack

bytecodeInterpreter | #define ARRAY_STOREFROM64 | CASE(_monitorenter)

源码: https://github.com/openjdk/jdk8u/blob/master/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp#L1816-L1921

CASE(_monitorenter): {
  // 获取锁对象
  oop lockee = STACK_OBJECT(-1);
  // derefing's lockee ought to provoke implicit null check
  CHECK_NULL(lockee);
  // find a free monitor or one already allocated for this object
  // if we find a matching object then we need a new monitor
  // since this is recursive enter
  BasicObjectLock* limit = istate->monitor_base();
  BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base();
  BasicObjectLock* entry = NULL;
  // 从栈底到栈顶遍历本地基础锁的栈,找到空闲的 BasicObjectLock (即 Lock Record)
  while (most_recent != limit ) {
    if (most_recent->obj() == NULL) entry = most_recent;
    else if (most_recent->obj() == lockee) break;
    most_recent++;
  }
  // 找到空闲的 BasicObjectLock (即 Lock Record)
  if (entry != NULL) {
    // BasicObjectLock->_obj 设置锁对象
    entry->set_obj(lockee);
    int success = false;
    // 获取 epoc 位置
    uintptr_t epoch_mask_in_place = (uintptr_t)markOopDesc::epoch_mask_in_place;

    // 获取锁对象 markword
    markOop mark = lockee->mark();
    intptr_t hash = (intptr_t) markOopDesc::no_hash;
    // mark word 是否有偏向锁标志位,即 101
    // implies UseBiasedLocking
    if (mark->has_bias_pattern()) {
      uintptr_t thread_ident;
      uintptr_t anticipated_bias_locking_value;
      // 线程的指针
      thread_ident = (uintptr_t)istate->thread();
      anticipated_bias_locking_value =
        (((uintptr_t)lockee->klass()->prototype_header() | thread_ident) ^ (uintptr_t)mark) &
        ~((uintptr_t) markOopDesc::age_mask_in_place);

      // anticipated_bias_locking_value==0 代表代表偏向线程就是当前线程,或者重入
      //    1. markword 的线程 ID 就是当前线程
      //    2. 而且「锁对象」的 markword 的 epoch 等于「锁对象的类」的 epoch
      if  (anticipated_bias_locking_value == 0) {
        // already biased towards this thread, nothing to do
        if (PrintBiasedLockingStatistics) {
          (* BiasedLocking::biased_lock_entry_count_addr())++;
        }
        // 这里设置 sucess 是为了不走下面的「轻量级锁」的逻辑
        success = true;
        // 说明是偏向锁重入,也就是之前已经分配了一个BasicObjectLock,那么执行到这里就结束了。
        // 该对象有几个BasicObjectLock,就表示重入了几次。
      }
      // 如果偏向模式关闭,则「撤销」偏向锁
      else if ((anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) != 0) {
        // try revoke bias
        // 获取「锁对象类」的 markword 对象
        markOop header = lockee->klass()->prototype_header();
        if (hash != markOopDesc::no_hash) {
          header = header->copy_set_hash(hash);
        }
        // CAS 将锁对象的 markword 替换为 class 中的 markword
        if (Atomic::cmpxchg_ptr(header, lockee->mark_addr(), mark) == mark) {
          if (PrintBiasedLockingStatistics)
            (*BiasedLocking::revoked_lock_entry_count_addr())++;
        }
      }
      // 如果 epoch 已过期,则需要重偏向
      else if ((anticipated_bias_locking_value & epoch_mask_in_place) !=0) {
        // try rebias
        // 创建一个偏向当前线程的 markword
        markOop new_header = (markOop) ( (intptr_t) lockee->klass()->prototype_header() | thread_ident);
        if (hash != markOopDesc::no_hash) {
          new_header = new_header->copy_set_hash(hash);
        }
        // CAS 替换当前锁对象的 markword
        if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), mark) == mark) {
          if (PrintBiasedLockingStatistics)
            (* BiasedLocking::rebiased_lock_entry_count_addr())++;
        }
        // 重偏向失败, 说明存在多线程竞争,则调用 monitorenter 进行锁升级
        else {
          CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
        }
        // 这里设置 sucess 是为了不走下面的「轻量级锁」的逻辑
        success = true;
      }
      // 1. 要么匿名偏向 2. 要么「已经偏向别的线程」
      else {
        // try to bias towards thread in case object is anonymously biased
        // 构建匿名偏向 (即线程 ID 为 0)的 markword
        markOop header = (markOop) ((uintptr_t) mark & ((uintptr_t)markOopDesc::biased_lock_mask_in_place |
                                                        (uintptr_t)markOopDesc::age_mask_in_place |
                                                        epoch_mask_in_place));
        if (hash != markOopDesc::no_hash) {
          header = header->copy_set_hash(hash);
        }
        // 加上当前线程 id
        markOop new_header = (markOop) ((uintptr_t) header | thread_ident);
        // debugging hint
        DEBUG_ONLY(entry->lock()->set_displaced_header((markOop) (uintptr_t) 0xdeaddead);)
        // CAS 将匿名偏向 (即线程 ID 为 0) 的 markword 替换为「偏向当前线程的 markword」
        if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), header) == header) {
          if (PrintBiasedLockingStatistics)
            (* BiasedLocking::anonymously_biased_lock_entry_count_addr())++;
        }
        // 失败,证明存在多线程竞争,,则调用 monitorenter 进行锁升级
        else {
          CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
        }
        // 这里设置 sucess 是为了不走下面的「轻量级锁」的逻辑
        success = true;
      }
    }

    // 1. 要么偏向标识为 0 2. 要么是偏向锁的撤销
    // 轻量级锁逻辑
    // traditional lightweight locking
    if (!success) {
      // 将当前锁对象的 mark word 与 01 无锁标识,构建新的 markword 且是无锁的
      markOop displaced = lockee->mark()->set_unlocked();
      // 让指向当前对象的 BasicObjectLock->_lock->_displaced_header 保存该锁对象的无锁 markword
      entry->lock()->set_displaced_header(displaced);
      // UseHeavyMonitors是虚拟机可配置项,表示是否直接开启重量级锁,并禁用偏向和轻量锁
      bool call_vm = UseHeavyMonitors;
      // UseHeavyMonitors 开启或者 CAS 尝试将锁对象的 markword 替换为 BasicObjectLock (Lock Record) 的地址,前提是 markword 是无锁状态
      if (call_vm || Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced) {
        // Is it simple recursive case?
        if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {
          // 这里是 UseHeavyMonitors 关闭,虽然 CAS 失败,再次判断当前线程是否拥有锁对象,是代表锁重入
          // 则直接将 Displaced Mark Word 设置为 null
          // 轻量级锁重入是使用 lock record 的数量来计入的
          entry->lock()->set_displaced_header(NULL);
        } else {
          // UseHeavyMonitors 开启或者,CAS 失败 (说明发生锁竞争,即锁对象不是无锁状态)
          // 走锁升级逻辑
          CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
        }
      }
    }
    UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
  } else {
    // 没拿到 lock record,重新执行
    istate->set_msg(more_monitors);
    UPDATE_PC_AND_RETURN(0); // Re-execute
  }
}

能获取偏向锁的条件

执行轻量级锁的逻辑条件

执行 InterpreterRuntime::monitorenter 方法的条件

竞争偏向锁失败,让 VM 线程进行偏向锁撤销为无锁或轻量级锁

CASE(_monitorenter)::bytecodeInterpreter
  |-lockee = STACK_OBJECT(-1) // 获取锁对象
  |-if (entry != NULL) // 找到空闲的 BasicObjectLock (即 Lock Record)
  |   |-entry->set_obj(lockee) // BasicObjectLock->_obj 设置锁对象
  |   |-success = false
  |   |-mark = lockee->mark() // 获取锁对象 markword
  |   |-if (mark->has_bias_pattern()) // 锁对象 mark word 是否有偏向锁标志位,即 101
  |   |   |-if  (anticipated_bias_locking_value == 0) // markword 的线程 ID 就是当前线程而且「锁对象」的 markword 的 epoch 等于「锁对象的类」的 epoch
  |   |   |-else if ((anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) != 0)  // 如果偏向模式关闭,则「撤销」偏向锁
  |   |   |-else if ((anticipated_bias_locking_value & epoch_mask_in_place) !=0) // 如果 epoch 已过期,则需要重偏向
  |   |   |-else { // 1. 要么匿名偏向 2. 要么「已经偏向别的线程」
  |   |   |   | // CAS 操作尝试以「匿名偏向 (即线程 ID 为 0) 的 markword」作为比较值,以「偏向当前线程的 markword」作为替换值,设置「锁对象的 markword」值
  |   |   |   |-if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), header) == header)
  |   |   |   |-else // CAS 失败,证明存在多线程竞争,,则调用 monitorenter 进行锁升级
  |   |   |   |   |-CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception)
  |   |   |   |   |   | |-monitorenter(THREAD, entry)::InterpreterRuntime
  |   |   |   |   |   |   |-if (UseBiasedLocking) // jvm 可配置参数是否开启了偏向锁
  |   |   |   |   |   |   |   |-fast_enter(h_obj, elem->lock(), true, CHECK)::ObjectSynchronizer
  |   |   |   |   |   |   |   |   |-if (UseBiasedLocking) // jvm 可配置参数是否开启了偏向锁
  |   |   |   |   |   |   |   |   |   |-if (!SafepointSynchronize::is_at_safepoint()) // 当前不处于 safepoint (正常 Java 线程走这里)
  |   |   |   |   |   |   |   |   |   |   |-cond = revoke_and_rebias(obj, attempt_rebias, THREAD)::BiasedLocking // 执行 revoke_and_rebias 方法,即执行「撤销」或「重偏向」
  |   |   |   |   |   |   |   |   |   |   |   |-heuristics = update_heuristics(obj(), attempt_rebias)
  |   |   |   |   |   |   |   |   |   |   |   |-if (heuristics == HR_SINGLE_REVOKE) // // 撤销单个线程 **最常见的执行分支**
  |   |   |   |   |   |   |   |   |   |   |   |   |-if (mark->biased_locker() == THREAD && prototype_header->bias_epoch() == mark->bias_epoch()) // 如果当前线程是偏向线程
  |   |   |   |   |   |   |   |   |   |   |   |   |-else  // 因为当前线程不是偏向线程,所以由 VMThread 执行撤销 revoke,最终会在 safepoint 调用 revoke_bias 方法撤销偏向
  |   |   |   |   |   |   |   |   |   |   |   |   |   |-VM_RevokeBias revoke(&obj, (JavaThread*) THREAD)
  |   |   |   |   |   |   |   |   |   |   |   |   |   |-VMThread::execute(&revoke)
  |   |   |   |   |   |   |   |   |   |   |   |   |   |   | // 判断偏向线程 T1 是否还存活
  |   |   |   |   |   |   |   |   |   |   |   |   |   |   | //  不存活,若允许重偏向,设置 markword 匿名偏向,不允许则设置 markword 为无锁模式,都返回 BIAS_REVOKED
  |   |   |   |   |   |   |   |   |   |   |   |   |   |   | //  存活且还在同步代块里,需要升级为轻量级锁,直接修改偏向线程栈中的 Lock Record。
  |   |   |   |   |   |   |   |   |   |   |   |   |   |   | //  存活且不在同步代块里,若允许重偏向,设置 markword 匿名偏向,不允许则设置 markword 为无锁模式,都返回 BIAS_REVOKED

InterpreterRuntime::monitorenter 方法

源码: https://github.com/openjdk/jdk8u/blob/master/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp#L620-L641

IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem))
#ifdef ASSERT
  thread->last_frame().interpreter_frame_verify_monitor(elem);
#endif
  if (PrintBiasedLockingStatistics) {
    Atomic::inc(BiasedLocking::slow_path_entry_count_addr());
  }
  Handle h_obj(thread, elem->obj());
  assert(Universe::heap()->is_in_reserved_or_null(h_obj()),
         "must be NULL or an object");
  // jvm 可配置参数是否开启了偏向锁
  if (UseBiasedLocking) {
    // Retry fast entry if bias is revoked to avoid unnecessary inflation
    // 开启了偏向锁走的就是 ObjectSynchronizer::fast_enter 的方法
    ObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK);
  } else {
    ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK);
  }
  assert(Universe::heap()->is_in_reserved_or_null(elem->obj()),
         "must be NULL or an object");
#ifdef ASSERT
  thread->last_frame().interpreter_frame_verify_monitor(elem);
#endif
IRT_END

ObjectSynchronizer::fast_enter 方法

源码: https://github.com/openjdk/jdk8u/blob/master/hotspot/src/share/vm/runtime/synchronizer.cpp#L169-L184

void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS) {
 // jvm 可配置参数是否开启了偏向锁
 if (UseBiasedLocking) {
    // 当前不处于 safepoint (正常 Java 线程走这里)
    if (!SafepointSynchronize::is_at_safepoint()) {
      // 执行 revoke_and_rebias 方法,即执行「撤销」和「重偏向」
      BiasedLocking::Condition cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD);
      // 返回值是 BIAS_REVOKED_AND_REBIASED (即等于 3),直接返回,否则执行 slow_enter 方法
      if (cond == BiasedLocking::BIAS_REVOKED_AND_REBIASED) {
        return;
      }
    } else { // 处于 safepoint (VM 线程会走这里)
      assert(!attempt_rebias, "can not rebias toward VM thread");
      // 执行 revoke_at_safepoint 方法,即执行在 safe point 进行撤销
      BiasedLocking::revoke_at_safepoint(obj);
      // 会执行下面 slow_enter 方法
    }
    assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");
 }
 // 或者 “JVM 没有开启偏向锁”
 // 或者 “JVM 开启偏向锁” + “不处于 safepoint” + “调用 BiasedLocking::revoke_and_rebias” 返回不等于 BIAS_REVOKED_AND_REBIASED
 // 或者 “JVM 开启偏向锁” + “处于 safepoint”
 // 上面任一条件满足都会执行 slow_enter 方法
 slow_enter (obj, lock, THREAD) ;
}

BiasedLocking#Condition 枚举定义

源码: https://github.com/openjdk/jdk8u/blob/master/hotspot/src/share/vm/runtime/biasedLocking.hpp#L161-L165

  enum Condition {
    NOT_BIASED = 1,
    BIAS_REVOKED = 2,
    BIAS_REVOKED_AND_REBIASED = 3
  };

BiasedLocking::revoke_and_rebias 撤销和重偏向方法

源码: https://github.com/openjdk/jdk8u/blob/master/hotspot/src/share/vm/runtime/biasedLocking.cpp#L554-L676

BiasedLocking::Condition BiasedLocking::revoke_and_rebias(Handle obj, bool attempt_rebias, TRAPS) {
  assert(!SafepointSynchronize::is_at_safepoint(), "must not be called while at safepoint");

  // 获取锁对象的 markword
  markOop mark = obj->mark();
  // 如果是匿名偏向 (ThreadID=0) 且 attempt_rebias (是否运行重偏向) 为 false
  // 如锁对象的hashcode方法被调用会出现这种情况,需要撤销偏向锁。
  if (mark->is_biased_anonymously() && !attempt_rebias) {
    markOop biased_value       = mark;
    // 创建无锁的 markword (hash 置为 0 & 锁状态置为无锁(001)& 分代年龄置为当前对象的分代[nohash|age|001])
    markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age());
    // 以「无锁 markword」作为替换值,以「锁对象的 wordword」作为比较值,CAS 设置「锁对象的 markword 内存」
    markOop res_mark = (markOop) Atomic::cmpxchg_ptr(unbiased_prototype, obj->mark_addr(), mark);
    // CAS 成功
    if (res_mark == biased_value) {
      // 返回「偏向锁撤销状态 (BIAS_REVOKED=2)」,会调用 slow_enter 方法
      return BIAS_REVOKED;
    }
  } else if (mark->has_bias_pattern()) { // 若锁对象 markword 有偏向锁标识 (101)
    Klass* k = obj->klass();
    markOop prototype_header = k->prototype_header();
    // 锁对象的类 Class 的对象的 markword 关闭了偏向锁,即 markword 没有偏向标识 (101)
    if (!prototype_header->has_bias_pattern()) {
      markOop biased_value       = mark;
      // 以「锁对象类的 markword」作为替换值,以「锁对象的 wordword」作为比较值,CAS 设置「锁对象的 markword 内存」
      markOop res_mark = (markOop) Atomic::cmpxchg_ptr(prototype_header, obj->mark_addr(), mark);
      assert(!(*(obj->mark_addr()))->has_bias_pattern(), "even if we raced, should still be revoked");
      // 不管 CAS 成功或失败,都返回「偏向锁撤销状态 (BIAS_REVOKED=2)」,会调用 slow_enter 方法
      return BIAS_REVOKED;
    } else if (prototype_header->bias_epoch() != mark->bias_epoch()) { // 锁对象的 markword 的 epoch 过期
      // 是否进行重偏向
      if (attempt_rebias) {
        assert(THREAD->is_Java_thread(), "");
        markOop biased_value       = mark;
        markOop rebiased_prototype = markOopDesc::encode((JavaThread*) THREAD, mark->age(), prototype_header->bias_epoch());
        // 以「重偏向当前线程 markword」作为替换值,以「锁对象的 wordword」作为比较值,CAS 设置「锁对象的 markword 内存」
        markOop res_mark = (markOop) Atomic::cmpxchg_ptr(rebiased_prototype, obj->mark_addr(), mark);
        // 重偏向成功,返回「偏向锁撤销且重偏向 (BIAS_REVOKED_AND_REBIASED=3)」,不会调用 slow_enter 方法
        if (res_mark == biased_value) {
          return BIAS_REVOKED_AND_REBIASED;
        }
      } else { // 不需要进行重偏向
        markOop biased_value       = mark;
        // 创建无锁的 markword (hash 置为 0 & 锁状态置为无锁(001)& 分代年龄置为当前对象的分代[nohash|age|001])
        markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age());
        // 以「无锁 markword」作为替换值,以「锁对象的 wordword」作为比较值,CAS 设置「锁对象的 markword 内存」
        markOop res_mark = (markOop) Atomic::cmpxchg_ptr(unbiased_prototype, obj->mark_addr(), mark);
        // CAS 成功,返回「偏向锁撤销状态 (BIAS_REVOKED=2)」,会调用 slow_enter 方法
        if (res_mark == biased_value) {
          return BIAS_REVOKED;
        }
      }
    }
  }

  // 执行到这里有以下两种情况:1. 锁对象不是偏向模式 2. 上面的 cas 操作失败
  // 批量重偏向与批量撤销的逻辑
  HeuristicsResult heuristics = update_heuristics(obj(), attempt_rebias);
  // 锁对象的 markword 不是偏向锁,返回「不是偏向锁 (NOT_BIASED=1)」,会执行 slow_enter
  if (heuristics == HR_NOT_BIASED) {
    return NOT_BIASED;
  } else if (heuristics == HR_SINGLE_REVOKE) { // 撤销单个线程 **最常见的执行分支**
    Klass *k = obj->klass();
    markOop prototype_header = k->prototype_header();
    // 锁对象的 markword 的偏向 thread_id 是当前线程 & epoch 没有过期
    // 走到这里说明需要撤销的是偏向当前线程的锁,当调用 Object#hashcode 方法时会走到这一步
    // 因为只要遍历当前线程的栈就好了,所以不需要等到 safepoint 再撤销。
    if (mark->biased_locker() == THREAD &&
        prototype_header->bias_epoch() == mark->bias_epoch()) {
      ResourceMark rm;
      if (TraceBiasedLocking) {
        tty->print_cr("Revoking bias by walking my own stack:");
      }
      EventBiasedLockSelfRevocation event;
      // 调用 revoke_bias 撤销偏向锁
      BiasedLocking::Condition cond = revoke_bias(obj(), false, false, (JavaThread*) THREAD, NULL);
      ((JavaThread*) THREAD)->set_cached_monitor_info(NULL);
      assert(cond == BIAS_REVOKED, "why not?");
      if (event.should_commit()) {
        event.set_lockClass(k);
        event.commit();
      }
      // 返回「偏向锁撤销状态 (BIAS_REVOKED=2)」,会调用 slow_enter 方法
      return cond;
    } else {
      EventBiasedLockRevocation event;
      // 由 VMThread 执行撤销 revoke,最终会在 safepoint 调用 revoke_bias 方法撤销偏向
      VM_RevokeBias revoke(&obj, (JavaThread*) THREAD);
      VMThread::execute(&revoke);
      if (event.should_commit() && (revoke.status_code() != NOT_BIASED)) {
        event.set_lockClass(k);
        // Subtract 1 to match the id of events committed inside the safepoint
        event.set_safepointId(SafepointSynchronize::safepoint_counter() - 1);
        event.set_previousOwner(revoke.biased_locker());
        event.commit();
      }
      // 返回结果
      return revoke.status_code();
    }
  }

  // 批量撤销、批量重偏向的逻辑
  assert((heuristics == HR_BULK_REVOKE) ||
         (heuristics == HR_BULK_REBIAS), "?");
  EventBiasedLockClassRevocation event;
  VM_BulkRevokeBias bulk_revoke(&obj, (JavaThread*) THREAD,
                                (heuristics == HR_BULK_REBIAS),
                                attempt_rebias);
  VMThread::execute(&bulk_revoke);
  if (event.should_commit()) {
    event.set_revokedClass(obj->klass());
    event.set_disableBiasing((heuristics != HR_BULK_REBIAS));
    // Subtract 1 to match the id of events committed inside the safepoint
    event.set_safepointId(SafepointSynchronize::safepoint_counter() - 1);
    event.commit();
  }
  return bulk_revoke.status_code();
}

BiasedLocking::update_heuristics 更新 heuristic方法

源码: https://github.com/openjdk/jdk8u/blob/master/hotspot/src/share/vm/runtime/biasedLocking.cpp#L278-L328

static HeuristicsResult update_heuristics(oop o, bool allow_rebias) {
  markOop mark = o->mark();
  // 判断是否是偏向模式
  if (!mark->has_bias_pattern()) {
    // 返回不是偏向标识
    return HR_NOT_BIASED;
  }

  Klass* k = o->klass();
  jlong cur_time = os::javaTimeMillis();
  // 获取该类上一次批量撤销的时间
  jlong last_bulk_revocation_time = k->last_biased_lock_bulk_revocation_time();
  // 获取该类偏向锁撤销的次数
  int revocation_count = k->biased_lock_revocation_count();
  // 如果已撤销的次数大于批量重偏向的次数,并小于批量撤销的次数
  // 并且撤销时间不为 0,也就是被初始化过,并且时间间隔大于重置的阈值
  if ((revocation_count >= BiasedLockingBulkRebiasThreshold) &&
      (revocation_count <  BiasedLockingBulkRevokeThreshold) &&
      (last_bulk_revocation_time != 0) &&
      (cur_time - last_bulk_revocation_time >= BiasedLockingDecayTime)) {
    // 将撤销的次数重置为 0
    k->set_biased_lock_revocation_count(0);
    revocation_count = 0;
  }

  // Make revocation count saturate just beyond BiasedLockingBulkRevokeThreshold
  // // 如果撤销的次数小于批量撤销次数 +1
  if (revocation_count <= BiasedLockingBulkRevokeThreshold) {
    revocation_count = k->atomic_incr_biased_lock_revocation_count();
  }

  // 如果达到「批量撤销阈值」则返回相应标识
  if (revocation_count == BiasedLockingBulkRevokeThreshold) {
    return HR_BULK_REVOKE;
  }

  // 如果达到「批量重偏向阈值」则返回相应标识
  if (revocation_count == BiasedLockingBulkRebiasThreshold) {
    return HR_BULK_REBIAS;
  }

  // 返回撤销单个对象的锁标识
  return HR_SINGLE_REVOKE;
}

BiasedLocking::revoke_bias 撤销偏向锁方法

源码: https://github.com/openjdk/jdk8u/blob/master/hotspot/src/share/vm/runtime/biasedLocking.cpp#L149-L267

static BiasedLocking::Condition revoke_bias(oop obj, bool allow_rebias, bool is_bulk, JavaThread* requesting_thread, JavaThread** biased_locker) {
  markOop mark = obj->mark();
  // 如果没有开启偏向模式即 markword 没有偏向标识 (101),则直接返回 NOT_BIASED
  if (!mark->has_bias_pattern()) {
    if (TraceBiasedLocking) {
      ResourceMark rm;
      tty->print_cr("  (Skipping revocation of object of type %s because it's no longer biased)",
                    obj->klass()->external_name());
    }
    return BiasedLocking::NOT_BIASED;
  }

  uint age = mark->age();
  // 构建两个 mark word,一个是匿名偏向模式(101),一个是无锁模式(001)
  markOop   biased_prototype = markOopDesc::biased_locking_prototype()->set_age(age);
  markOop unbiased_prototype = markOopDesc::prototype()->set_age(age);

  if (TraceBiasedLocking && (Verbose || !is_bulk)) {
    ResourceMark rm;
    tty->print_cr("Revoking bias of object " INTPTR_FORMAT " , mark " INTPTR_FORMAT " , type %s , prototype header " INTPTR_FORMAT " , allow rebias %d , requesting thread " INTPTR_FORMAT,
                  p2i((void *)obj), (intptr_t) mark, obj->klass()->external_name(), (intptr_t) obj->klass()->prototype_header(), (allow_rebias ? 1 : 0), (intptr_t) requesting_thread);
  }

  JavaThread* biased_thread = mark->biased_locker();
  // 锁对象 markwork 的是匿名偏向
  if (biased_thread == NULL) {
    // 不允许重偏向
    if (!allow_rebias) {
      // 设置锁对象 markword 为无锁模式 (001)
      obj->set_mark(unbiased_prototype);
    }
    if (TraceBiasedLocking && (Verbose || !is_bulk)) {
      tty->print_cr("  Revoked bias of anonymously-biased object");
    }
    return BiasedLocking::BIAS_REVOKED;
  }

  // Handle case where the thread toward which the object was biased has exited
  // 判断偏向线程是否还存活
  bool thread_is_alive = false;
  // 如果当前线程就是偏向线程,设置存活
  if (requesting_thread == biased_thread) {
    thread_is_alive = true;
  } else {
    // 当前线程不是偏向线程,遍历当前jvm的所有线程,如果能找到,则说明偏向的线程还存活
    for (JavaThread* cur_thread = Threads::first(); cur_thread != NULL; cur_thread = cur_thread->next()) {
      if (cur_thread == biased_thread) {
        thread_is_alive = true;
        break;
      }
    }
  }
  // 如果偏向的线程已经不存活了
  if (!thread_is_alive) {
    // 允许重偏向
    if (allow_rebias) {
      // 设置将锁对象 mark word 设置为匿名偏向状态
      obj->set_mark(biased_prototype);
    } else {
       // 设置将锁对象 mark word 设置为无锁模式
      obj->set_mark(unbiased_prototype);
    }
    if (TraceBiasedLocking && (Verbose || !is_bulk)) {
      tty->print_cr("  Revoked bias of object biased toward dead thread");
    }
    return BiasedLocking::BIAS_REVOKED;
  }

  // 线程还存活则遍历线程栈中所有的 Lock Record
  GrowableArray<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(biased_thread);
  BasicLock* highest_lock = NULL;
  for (int i = 0; i < cached_monitor_info->length(); i++) {
    MonitorInfo* mon_info = cached_monitor_info->at(i);
    // 如果能找到对应的 Lock Record 说明偏向的线程还在执行同步代码块中的代码
    if (mon_info->owner() == obj) {
      if (TraceBiasedLocking && Verbose) {
        tty->print_cr("   mon_info->owner (" PTR_FORMAT ") == obj (" PTR_FORMAT ")",
                      p2i((void *) mon_info->owner()),
                      p2i((void *) obj));
      }
      // Assume recursive case and fix up highest lock later
      // 需要升级为轻量级锁,直接修改偏向线程栈中的 Lock Record。
      // 为了处理锁重入的 case,在这里将 Lock Record 的 Displaced Mark Word 设置为 null,第一个 Lock Record 会在下面的代码中再处理
      markOop mark = markOopDesc::encode((BasicLock*) NULL);
      highest_lock = mon_info->lock();
      highest_lock->set_displaced_header(mark);
    } else {
      if (TraceBiasedLocking && Verbose) {
        tty->print_cr("   mon_info->owner (" PTR_FORMAT ") != obj (" PTR_FORMAT ")",
                      p2i((void *) mon_info->owner()),
                      p2i((void *) obj));
      }
    }
  }
  if (highest_lock != NULL) {
    // 修改第一个 Lock Record 为无锁状态,然后将 obj 的 mark word 设置为指向该 Lock Record 的指针
    highest_lock->set_displaced_header(unbiased_prototype);
    obj->release_set_mark(markOopDesc::encode(highest_lock));
    assert(!obj->mark()->has_bias_pattern(), "illegal mark state: stack lock used bias bit");
    if (TraceBiasedLocking && (Verbose || !is_bulk)) {
      tty->print_cr("  Revoked bias of currently-locked object");
    }
  } else {
    // 走到这里说明偏向线程已经不在同步块中了
    if (TraceBiasedLocking && (Verbose || !is_bulk)) {
      tty->print_cr("  Revoked bias of currently-unlocked object");
    }
    // 允许重偏向,设置为匿名偏向状态
    if (allow_rebias) {
      obj->set_mark(biased_prototype);
    } else {
      // 不允许重偏向,设置为无锁模式 (001)
      // Store the unlocked value into the object's header.
      obj->set_mark(unbiased_prototype);
    }
  }

#if INCLUDE_JFR
  // If requested, return information on which thread held the bias
  if (biased_locker != NULL) {
    *biased_locker = biased_thread;
  }
#endif // INCLUDE_JFR

  return BiasedLocking::BIAS_REVOKED;
}

释放偏向锁

CASE(_monitorexit)::bytecodeInterpreter

bytecodeInterpreter 方法 bytecodeInterpreter | #define ARRAY_STOREFROM64 | CASE(_monitorexit)

源码: https://github.com/openjdk/jdk8u/blob/master/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp#L1923-L1953

CASE(_monitorexit): {
  oop lockee = STACK_OBJECT(-1); // 锁对象
  CHECK_NULL(lockee);
  BasicObjectLock* limit = istate->monitor_base();
  BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base();
  while (most_recent != limit ) { // 从栈底遍历到栈顶
    // 找到 BasicObjectLock (即 LockRecord) 的 obj 等于锁对象
    if ((most_recent)->obj() == lockee) {
      // 获取锁
      BasicLock* lock = most_recent->lock();
      markOop header = lock->displaced_header();
      // 设置将 BasicObjectLock (即 LockRecord) 中的_obj设置为空
      // 如果是偏向锁,只需要释放 BasicObjectLock (即 LockRecord) 就行
      most_recent->set_obj(NULL);
      if (!lockee->mark()->has_bias_pattern()) { // 如果不是偏向锁,走轻量级或者重量级锁的释放流程
        bool call_vm = UseHeavyMonitors;
        // If it isn't recursive we either must swap old header or call the runtime
        if (header != NULL || call_vm) { // header 为空即代表轻量级锁重入,轻量级锁只需要处理第一次 Lock Record 
          if (call_vm || Atomic::cmpxchg_ptr(header, lockee->mark_addr(), lock) != lock) { // CAS 设置为原来 markword (即存放在 Lock Record 的 markword)
            // restore object for the slow case
            most_recent->set_obj(lockee);
            CALL_VM(InterpreterRuntime::monitorexit(THREAD, most_recent), handle_exception);
          }
        }
      }
      UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); // 执行下一条命令
    }
    most_recent++;
  }
  CALL_VM(InterpreterRuntime::throw_illegal_monitor_state_exception(THREAD), handle_exception);
  ShouldNotReachHere();
}
monsterhxw commented 10 months ago

JDK 1.6 加入偏向锁,但默认是延迟开启 (4_000 ms)

-XX:BiasedLockingStartupDelay 参数可修改

JDK 15 之后默认关闭偏向锁

原因是偏向锁的撤销,是需要 VM 线程在 safepoint 进行单个/批量将具有偏向锁的锁对象,进行撤销为「无锁」或者为「轻量级锁」,会导致 STW 时间加长,性能不好