HotSpot VM重量级锁降级机制的实现原理

HotSpot VM重量级锁降级机制的实现原理

HotSpot VM内置锁的同步机制简述:

HotSpot VM采用三中不同的方式实现了对象监视器——Object Monitor,并且可以在这三种实现方式中自动切换。偏向锁通过在Java对象的对象头markOop中install一个JavaThread指针的方式实现了这个Java对象对此Java线程的偏向,并且只有该偏向线程能够锁定Lock该对象。但是只要有第二个Java线程企图锁定这个已被偏向的对象时,偏向锁就不再满足这种情况了,然后呢JVM就将Biased Locking切换成了Basic Locking(基本对象锁)。Basic Locking使用CAS操作确保多个Java线程在此对象锁上互斥执行。如果CAS由于竞争而失败(第二个Java线程试图锁定一个正在被其他Java线程持有的对象),这时基本对象锁因为不再满足需要从而JVM会切换到膨胀锁 - ObjectMonitor。不像偏向锁和基本对象锁的实现,重量级锁的实现需要在Native的Heap空间中分配内存,然后指向该空间的内存指针会被装载到Java对象中去。这个过程我们称之为锁膨胀。

降级的目的和过程:

因为BasicLocking的实现优先于重量级锁的使用,JVM会尝试在SWT的停顿中对处于“空闲(idle)”状态的重量级锁进行降级(deflate)。这个降级过程是如何实现的呢?我们知道在STW时,所有的Java线程都会暂停在“安全点(SafePoint)”,此时VMThread通过对所有Monitor的遍历,或者通过对所有依赖于MonitorInUseLists值的当前正在“使用”中的Monitor子序列进行遍历,从而得到哪些未被使用的“Monitor”作为降级对象。

可以降级的Monitor对象:

重量级锁的降级发生于STW阶段,降级对象就是那些仅仅能被VMThread访问而没有其他JavaThread访问的Monitor对象。

HotSpot VM中的实现:

上述降级过程在HotSpot VM中的实现,可以参考:

void ObjectSynchronizer::deflate_idle_monitors() {
  assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
  int nInuse = 0 ;              // currently associated with objects
  int nInCirculation = 0 ;      // extant
  int nScavenged = 0 ;          // reclaimed
  bool deflated = false;

  ObjectMonitor * FreeHead = NULL ;  // Local SLL of scavenged monitors
  ObjectMonitor * FreeTail = NULL ;

  TEVENT (deflate_idle_monitors) ;
// Prevent omFlush from changing mids in Thread dtor's during deflation
// And in case the vm thread is acquiring a lock during a safepoint
// See e.g. 6320749
  Thread::muxAcquire (&ListLock, "scavenge - return") ;

  if (MonitorInUseLists) {
    int inUse = 0;
    for (JavaThread* cur = Threads::first(); cur != NULL; cur = cur->next()) {
      nInCirculation+= cur->omInUseCount;
      int deflatedcount = walk_monitor_list(cur->omInUseList_addr(), &FreeHead, &FreeTail);
      cur->omInUseCount-= deflatedcount;
// verifyInUse(cur);
      nScavenged += deflatedcount;
      nInuse += cur->omInUseCount;
     }

// For moribund threads, scan gOmInUseList
   if (gOmInUseList) {
     nInCirculation += gOmInUseCount;
     int deflatedcount = walk_monitor_list((ObjectMonitor **)&gOmInUseList, &FreeHead, &FreeTail);
     gOmInUseCount-= deflatedcount;
     nScavenged += deflatedcount;
     nInuse += gOmInUseCount;
    }

  } else for (ObjectMonitor* block = gBlockList; block != NULL; block = next(block)) {
// Iterate over all extant monitors - Scavenge all idle monitors.
    assert(block->object() == CHAINMARKER, "must be a block header");
    nInCirculation += _BLOCKSIZE ;
    for (int i = 1 ; i < _BLOCKSIZE; i++) {
      ObjectMonitor* mid = &block[i];
      oop obj = (oop) mid->object();

      if (obj == NULL) {
// The monitor is not associated with an object.
// The monitor should either be a thread-specific private
// free list or the global free list.
// obj == NULL IMPLIES mid->is_busy() == 0
        guarantee (!mid->is_busy(), "invariant") ;
        continue ;
      }
      deflated = deflate_monitor(mid, obj, &FreeHead, &FreeTail);

      if (deflated) {
        mid->FreeNext = NULL ;
        nScavenged ++ ;
      } else {
        nInuse ++;
      }
    }
  }

  MonitorFreeCount += nScavenged;

// Consider: audit gFreeList to ensure that MonitorFreeCount and list agree.

  if (ObjectMonitor::Knob_Verbose) {
    ::printf ("Deflate: InCirc=%d InUse=%d Scavenged=%d ForceMonitorScavenge=%d : pop=%d free=%d\n",
        nInCirculation, nInuse, nScavenged, ForceMonitorScavenge,
        MonitorPopulation, MonitorFreeCount) ;
    ::fflush(stdout) ;
  }

  ForceMonitorScavenge = 0;    // Reset

// Move the scavenged monitors back to the global free list.
  if (FreeHead != NULL) {
     guarantee (FreeTail != NULL && nScavenged > 0, "invariant") ;
     assert (FreeTail->FreeNext == NULL, "invariant") ;
// constant-time list splice - prepend scavenged segment to gFreeList
     FreeTail->FreeNext = gFreeList ;
     gFreeList = FreeHead ;
  }
  Thread::muxRelease (&ListLock) ;

  if (ObjectMonitor::_sync_Deflations != NULL) ObjectMonitor::_sync_Deflations->inc(nScavenged) ;
  if (ObjectMonitor::_sync_MonExtant  != NULL) ObjectMonitor::_sync_MonExtant ->set_value(nInCirculation);

// TODO: Add objectMonitor leak detection.
// Audit/inventory the objectMonitors -- make sure they're all accounted for.
  GVars.stwRandom = os::random() ;
  GVars.stwCycle ++ ;
}

补充说明:JEP - Concurrent Monitor Deflation,旨在通过并发手段解决重量级锁降级过程中的效率问题,可以参考:JEP draft: Concurrent Monitor Deflation

编辑于 2017-08-14 14:31