monsterhxw / my-notes

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

Java synchronized 的 Light-weight Lock (轻量级锁) 源码分析 #2

Open monsterhxw opened 9 months ago

monsterhxw commented 9 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 (!success) // // 轻量级锁逻辑 1. 要么偏向标识为 0; 2. 要么是偏向锁的撤销;
  |   |   |-markOop displaced = lockee->mark()->set_unlocked(); // 将当前锁对象的 mark word 与 001 无锁标识,构建新的 markword 且是无锁的
  |   |   |-entry->lock()->set_displaced_header(displaced); // 让指向当前对象的 BasicObjectLock->_lock->_displaced_header 保存该锁对象的无锁 markword
  |   |   | // UseHeavyMonitors 开启或者 CAS 尝试将锁对象的 markword 替换为 BasicObjectLock (Lock Record) 的地址,前提是 markword 是无锁状态
  |   |   |-if (call_vm || Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced)
  |   |   |   |-if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits()))
  |   |   |   |   | // 这里是 UseHeavyMonitors 关闭,虽然 CAS 失败,再次判断当前线程是否拥有锁对象,是代表锁重入
  |   |   |   |   |-entry->lock()->set_displaced_header(NULL); // 则直接将 Displaced Mark Word 设置为 null,轻量级锁重入是使用 lock record 的数量来计入的
  |   |   |   |-else // UseHeavyMonitors 开启或者,CAS 失败 (说明发生锁竞争,即锁对象不是无锁状态),走锁升级逻辑
  |   |   |   |   |-CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception)

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); // 获取锁对象
  CHECK_NULL(lockee);
  BasicObjectLock* limit = istate->monitor_base();
  BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base();
  BasicObjectLock* entry = NULL;
  while (most_recent != limit ) { // 从栈底到栈顶遍历本地基础锁的栈,找到空闲的 BasicObjectLock (即 Lock Record)
    if (most_recent->obj() == NULL) entry = most_recent;
    else if (most_recent->obj() == lockee) break;
    most_recent++;
  }
  if (entry != NULL) { // 找到空闲的 BasicObjectLock (即 Lock Record)
    entry->set_obj(lockee); // BasicObjectLock->_obj 设置锁对象
    int success = false;
    uintptr_t epoch_mask_in_place = (uintptr_t)markOopDesc::epoch_mask_in_place; // 获取 epoch 位置
    markOop mark = lockee->mark(); // 获取锁对象 markword
    intptr_t hash = (intptr_t) markOopDesc::no_hash;
    // implies UseBiasedLocking
    if (mark->has_bias_pattern()) { // mark word 是否有偏向锁标志位,即 101
     // 省略,具体看偏向锁流程
    }
    // 轻量级锁逻辑 1. 要么偏向标识为 0; 2. 要么是偏向锁的撤销;
    if (!success) {
      markOop displaced = lockee->mark()->set_unlocked(); // 将当前锁对象的 mark word 与 001 无锁标识,构建新的 markword 且是无锁的
      entry->lock()->set_displaced_header(displaced); // 让指向当前对象的 BasicObjectLock->_lock->_displaced_header 保存该锁对象的无锁 markword
      bool call_vm = UseHeavyMonitors; // 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
  }
}

竞争轻量级锁

CASE(_monitorenter)::bytecodeInterpreter
  |-monitorenter(thread, elem)::InterpreterRuntime
  |   |-if (UseBiasedLocking)
  |   |   |-fast_enter(h_obj, elem->lock(), true, CHECK)::ObjectSynchronizer
  |   |   |   |-if (UseBiasedLocking)
  |   |   |   |   |-if (!SafepointSynchronize::is_at_safepoint())
  |   |   |   |   |   |-cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD)
  |   |   |   |   |   |   |-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)
  |   |   |   |-slow_enter(obj, lock, THREAD)::ObjectSynchronizer // 所以最终还是走到 slow_enter 方法

ObjectSynchronizer::slow_enter 方法分析

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

void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) {
  markOop mark = obj->mark();
  // 锁对象的 markword 是无锁模式,
  // case 1: markword 偏向锁标识 (101) 且不是匿名偏向也不是偏向当前线程,锁对象偏向的线程不存活,进行偏向锁撤销为无锁模式 (001)
  // case 2: markword 偏向锁标识 (101) 且不是匿名偏向也不是偏向当前线程,锁对象偏向的线程存活,但是不在同步代码块里,进行偏向锁撤销为无锁模式 (001)
  if (mark->is_neutral()) {
    // 设置 BasicObjectLock (即 Lock Record) 的 _lock->_displaced_header 值为 markword
    lock->set_displaced_header(mark);
    // 以「Lock Record 地址」作为替换值,以「锁对象的 wordword」作为比较值,CAS 设置「锁对象的 markword 内存」,
    if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) {
      // CAS 成功,说明获取到「轻量级锁」,直接返回执行「同步代码块代码」
      TEVENT (slow_enter: release stacklock) ;
      return ;
      }
  } else if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) { // 是轻量级锁 (0) 且锁对象的线程地址等于当前线程,说明轻量级锁重入
    assert(lock != mark->locker(), "must not re-lock the same lock");
    assert(lock != (BasicLock*)obj->mark(), "don't relock with same BasicLock");
    // 为了处理锁重入的 case,在这里将 Lock Record 的 Displaced Mark Word 设置为 null
    // 轻量级锁重入是使用 lock record 的数量来计入的
    lock->set_displaced_header(NULL);
    // 直接返回执行「同步代码块代码」
    return;
    }
  // case 1: markword 是无锁,但是上面的代码 CAS 失败,说明发生竞争,其他线程获取到「轻量级锁」
  // case 2: markword 是轻量级锁,且不是当前线程持有
  //     2.1 可能是前面偏向锁撤销成轻量级锁 (即偏向锁不是偏向当前线程,且偏向的线程存活,且还在同步代码块里)
  //     2.2 可能是轻量级锁,但不是重入,也不是偏向锁撤销成轻量级锁,而是有其他线程持有轻量级锁
  // case: 3 markword 是重量级锁
  lock->set_displaced_header(markOopDesc::unused_mark());
  // 走到这里说明发生轻量级锁竞争或者是重量级锁,需要膨胀为重量级锁,膨胀完再调用 ObjectMonitor::enter 方法
  ObjectSynchronizer::inflate(THREAD, obj(), inflate_cause_monitor_enter)->enter(THREAD); // inflate_cause_monitor_enter=1
}

释放轻量级锁

case(_monitorexit)::bytecodeInterpreter
  |-monitorexit(THREAD, most_recent)::InterpreterRuntime
  |   |-slow_exit(h_obj(), elem->lock(), thread)::ObjectSynchronizer
  |   |   |-fast_exit(object, lock, THREAD)::ObjectSynchronizer

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();
}

InterpreterRuntime::monitorexit 方法

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

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());
  assert(Universe::heap()->is_in_reserved_or_null(h_obj()),
         "must be NULL or an object");
  if (elem == NULL || h_obj()->is_unlocked()) {
    THROW(vmSymbols::java_lang_IllegalMonitorStateException());
  }
  // 直接调用 slow_exit
  ObjectSynchronizer::slow_exit(h_obj(), elem->lock(), thread);
  // Free entry. This must be done here, since a pending exception might be installed on
  // exit. If it is not cleared, the exception handling code will try to unlock the monitor again.
  elem->set_obj(NULL);
#ifdef ASSERT
  thread->last_frame().interpreter_frame_verify_monitor(elem);
#endif
IRT_END

ObjectSynchronizer::slow_exit 方法

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

void ObjectSynchronizer::slow_exit(oop object, BasicLock* lock, TRAPS) {
  fast_exit(object, lock, THREAD) ;
}

ObjectSynchronizer:: fast_exit 方法

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

void ObjectSynchronizer::fast_exit(oop object, BasicLock* lock, TRAPS) {
  assert(!object->mark()->has_bias_pattern(), "should not see bias pattern here");
  markOop dhw = lock->displaced_header();
  markOop mark ;
  // Lock Record 存放的 markword 为空,说明是重入的情况,这里面没有做实际的操作就返回了
  if (dhw == NULL) {
     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() ;
  // 锁对象的 markword 等于 Lock Record 的地址,说明是「轻量级锁」,进行 CAS 设置
  if (mark == (markOop) lock) {
     assert (dhw->is_neutral(), "invariant") ;
     // 以「Lock Record 存放的原 markword」作为替换值,以「锁对象的 wordword」作为比较值,CAS 设置「锁对象的 markword 内存」值,成功直接返回
     if ((markOop) Atomic::cmpxchg_ptr (dhw, object->mark_addr(), mark) == mark) {
        TEVENT (fast_exit: release stacklock) ;
        return;
     }
  }
  // 走到这里说明符合任一条件,case1: 当前是「重量级锁」 case2: 是轻量级锁,但 CAS 失败 (即解锁时发生竞争)
  // 需要膨胀为重量级锁,膨胀完再调用 ObjectMonitor::exit 方法
  ObjectSynchronizer::inflate(THREAD, object, inflate_cause_vm_internal)->exit(true, THREAD);
}