Смерть синхронизированная базовая реализация - легкий замок

задняя часть JVM GitHub C++

Эта статья реализует третью статью для базового синхронизированного лечения, содержание является легкой реализацией блокировки.

Легкие замки не сложные, многие из которых вБлокировка смещенияупоминается в статье,Будет некоторое совпадение с содержанием этой статьи.

Кроме того, фон и базовый процесс легкого блокировки находятся вВведениеобъяснил в.Настоятельно рекомендуется прочитать эту статью на основе прочтения двух статей.

В этой серии статей мы рассмотрим HotSpotsynchronizedВсесторонний анализ реализации блокировки, включая анализ принципа и исходного кода предвзятых блокировок, облегченных блокировок и тяжелых блокировок, блокировок, разблокировок и процессов обновления блокировок.synchronizedНекоторая помощь от однокурсников на дороге. В основном включают следующие статьи:

Реализация Deadly Synchronized Bottom — Введение

Реализация нижнего уровня Deadly Synchronized — предвзятая блокировка

Базовая реализация Deadly Synchronized — облегченная блокировка

Реализация нижнего уровня Deadly Synchronized — усиленная блокировка

Больше статей смотрите в личном блоге:GitHub.com/farmer Джон Брат…

Эта статья разделена на две части:

1. Облегченный процесс получения блокировки

2. Легкий процесс открытия замка

Версия JVM, которую я вижу, это jdk8u, конкретный номер версии и код можно найти вздесьВидеть.

Облегченный процесс получения блокировки

Приступим к анализу процесса получения легковесного замка, код находится вbytecodeInterpreter.cpp#1816.

CASE(_monitorenter): {
  oop lockee = STACK_OBJECT(-1);
  ...
  if (entry != NULL) {
   ...
   // 上面省略的代码中如果CAS操作失败也会调用到InterpreterRuntime::monitorenter

    // traditional lightweight locking
    if (!success) {
      // 构建一个无锁状态的Displaced Mark Word
      markOop displaced = lockee->mark()->set_unlocked();
      // 设置到Lock Record中去
      entry->lock()->set_displaced_header(displaced);
      bool call_vm = UseHeavyMonitors;
      if (call_vm || Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced) {
        // 如果CAS替换不成功,代表锁对象不是无锁状态,这时候判断下是不是锁重入
        // Is it simple recursive case?
        if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {
          entry->lock()->set_displaced_header(NULL);
        } else {
          // CAS操作失败则调用monitorenter
          CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
        }
      }
    }
    UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
  } else {
    istate->set_msg(more_monitors);
    UPDATE_PC_AND_RETURN(0); // Re-execute
  }
}

Если объект блокировки не привязан к режиму или был смещен к другим потокам, тогдаsuccessзаfalse. В это время будет построено свободное от блокировки состояние.mark wordустановлен вLock RecordИди, мы называемLock RecordОбъекты хранятся вmark wordПоле называетсяDisplaced Mark Word.

CAS дает сбой, если текущее состояние блокировки не является свободным от блокировки. Если это повторный вход в блокировку, тоLock RecordизDisplaced Mark WordУстановить какnull.

Мы видим демонстрацию, повторенную трижды в демонстрации при получении блокировки,

synchronized(obj){
    synchronized(obj){
    	synchronized(obj){
    	}
    }
}

Предполагая, что состояние замка является упрощенным, следующий рисунок отражаетmark wordи стек потоковLock Recordсостояние, вы можете видеть, что стек потока справа содержит 3 указателя на текущий объект блокировкиLock Record. Высший порядок в стекеLock RecordВыделяется при первом получении блокировки. ТотDisplaced Mark wordЗначением блокировки является объект блокировки.mark word, последующий повторный вход в блокировку выделит блокировку в стеке потокаDisplaced Mark wordзаnullизLock Record.

Почему JVM решила добавить в стек потоковDisplaced Mark wordнулевойLock Recordдля представления количества повторных входов? В первую очередь необходимо фиксировать количество повторных входов в замок, потому что каждой разблокировке нужно соответствовать замку, и когда количество разблокировок равно количеству замков, блокировка действительно снимается, то есть блокировка необходимо использовать при разблокировке количество записей. Простое решение — записывать количество повторных входов в блокировку в заголовке объекта.mark wordв, ноmark wordограничен по размеру и больше не может хранить информацию. Другой вариант — просто создатьLock RecordИ записывать в него количество повторных входов.Думаю, причина, по которой Hotspot этого не сделал, заключается в учете эффекта эффективности: каждый раз, когда повторный вход получает блокировку, необходимо пройтись по стеку потока, чтобы найти соответствующую блокировку.Lock Record, а затем измените его значение.

Так что, в конце концов, Hotspot добавляет блокировку каждый раз, когда получает блокировку.Lock RecordПредставлен переинтересованный замок.

см. далееInterpreterRuntime::monitorenterметод

IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem))
  ...
  Handle h_obj(thread, elem->obj());
  assert(Universe::heap()->is_in_reserved_or_null(h_obj()),
         "must be NULL or an object");
  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

fast_enterВ статье был проанализирован процесс смещения блокировки, если в настоящее время он находится в режиме смещения и смещенный поток все еще использует блокировку, он заблокирует блокировку.mark wordПерейдите в состояние облегченной блокировки и в то же время изменит стек смещенного потока.Lock RecordДоработан до формы, соответствующей облегченному замку. расположение кодаbiasedLocking.cpp#212.

 // 线程还存活则遍历线程栈中所有的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) {
      ...
      // 需要升级为轻量级锁,直接修改偏向线程栈中的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 (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));
    ...
  } else {
    ...
  }

мы видимslow_enterПроцесс.

void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) {
  markOop mark = obj->mark();
  assert(!mark->has_bias_pattern(), "should not see bias pattern here");
  // 如果是无锁状态
  if (mark->is_neutral()) {
    //设置Displaced Mark Word并替换对象头的mark word
    lock->set_displaced_header(mark);
    if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) {
      TEVENT (slow_enter: release stacklock) ;
      return ;
    }
  } else
  if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) {
    assert(lock != mark->locker(), "must not re-lock the same lock");
    assert(lock != (BasicLock*)obj->mark(), "don't relock with same BasicLock");
    // 如果是重入,则设置Displaced Mark Word为null
    lock->set_displaced_header(NULL);
    return;
  }

  ...
  // 走到这一步说明已经是存在多个线程竞争锁了 需要膨胀为重量级锁
  lock->set_displaced_header(markOopDesc::unused_mark());
  ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);
}

Облегченный процесс открытия замка

CASE(_monitorexit): {
  oop lockee = STACK_OBJECT(-1);
  CHECK_NULL(lockee);
  // derefing's lockee ought to provoke implicit null check
  // find our monitor slot
  BasicObjectLock* limit = istate->monitor_base();
  BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base();
  // 从低往高遍历栈的Lock Record
  while (most_recent != limit ) {
    // 如果Lock Record关联的是该锁对象
    if ((most_recent)->obj() == lockee) {
      BasicLock* lock = most_recent->lock();
      markOop header = lock->displaced_header();
      // 释放Lock Record
      most_recent->set_obj(NULL);
      // 如果是偏向模式,仅仅释放Lock Record就好了。否则要走轻量级锁or重量级锁的释放流程
      if (!lockee->mark()->has_bias_pattern()) {
        bool call_vm = UseHeavyMonitors;
        // header!=NULL说明不是重入,则需要将Displaced Mark Word CAS到对象头的Mark Word
        if (header != NULL || call_vm) {
          if (call_vm || Atomic::cmpxchg_ptr(header, lockee->mark_addr(), lock) != lock) {
            // CAS失败或者是重量级锁则会走到这里,先将obj还原,然后调用monitorexit方法
            most_recent->set_obj(lockee);
            CALL_VM(InterpreterRuntime::monitorexit(THREAD, most_recent), handle_exception);
          }
        }
      }
      //执行下一条命令
      UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
    }
    //处理下一条Lock Record
    most_recent++;
  }
  // Need to throw illegal monitor state exception
  CALL_VM(InterpreterRuntime::throw_illegal_monitor_state_exception(THREAD), handle_exception);
  ShouldNotReachHere();
}

Когда облегченный замок отпущен, его необходимоDisplaced Mark Wordзаменен заголовком объектаmark wordсередина. Если CAS не работает или является тяжеловесной блокировкой, введитеInterpreterRuntime::monitorexitметод.

//%note monitor_1
IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorexit(JavaThread* thread, BasicObjectLock* elem))
 
  Handle h_obj(thread, elem->obj());
  ...
  ObjectSynchronizer::slow_exit(h_obj(), elem->lock(), thread);
  // Free entry. This must be done here, since a pending exception might be installed on
  //释放Lock Record
  elem->set_obj(NULL);
  ...
IRT_END

monitorexitвызов завершенslow_exitметод, выпускLock Record.

void ObjectSynchronizer::slow_exit(oop object, BasicLock* lock, TRAPS) {
  fast_exit (object, lock, THREAD) ;
}
void ObjectSynchronizer::fast_exit(oop object, BasicLock* lock, TRAPS) {
  ...
  markOop dhw = lock->displaced_header();
  markOop mark ;
  if (dhw == NULL) {
     // 重入锁,什么也不做
   	 ...
     return ;
  }

  mark = object->mark() ;

  // 如果是mark word==Displaced Mark Word即轻量级锁,CAS替换对象头的mark word
  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;
     }
  }
  //走到这里说明是重量级锁或者解锁时发生了竞争,膨胀后调用重量级锁的exit方法。
  ObjectSynchronizer::inflate(THREAD, object)->exit (true, THREAD) ;
}

В этом методе сначала определите, является ли это облегченным замком, если это облегченный замок, он будет замененmark word, иначе раздуть в тяжеловесный замок и вызватьexitметод, и связанная с ним логика будет объяснена в статье о тяжелых замках.