--- old/src/hotspot/share/oops/markOop.hpp 2019-04-18 22:07:19.560307076 -0400 +++ new/src/hotspot/share/oops/markOop.hpp 2019-04-18 22:07:18.932307087 -0400 @@ -209,6 +209,10 @@ bool is_unlocked() const { return (mask_bits(value(), biased_lock_mask_in_place) == unlocked_value); } + // ObjectMonitor::install_displaced_markword_in_object() uses + // is_marked() on ObjectMonitor::_header as part of the restoration + // protocol for an object's header. In this usage, the mark bits are + // only ever set (and cleared) on the ObjectMonitor::_header field. bool is_marked() const { return (mask_bits(value(), lock_mask_in_place) == marked_value); } --- old/src/hotspot/share/runtime/objectMonitor.cpp 2019-04-18 22:07:21.048307050 -0400 +++ new/src/hotspot/share/runtime/objectMonitor.cpp 2019-04-18 22:07:20.348307062 -0400 @@ -300,9 +300,11 @@ // Ensure the object-monitor relationship remains stable while there's contention. const jint contentions = Atomic::add(1, &_contentions); if (contentions <= 0 && _owner == DEFLATER_MARKER) { - // Async deflation in progress. Help deflater thread install - // the mark word (in case deflater thread is slow). - install_displaced_markword_in_object(); + // Async deflation is in progress. Attempt to restore the + // header/dmw to the object's header so that we only retry once + // if the deflater thread happens to be slow. + const oop obj = (oop) object(); + install_displaced_markword_in_object(obj); Self->_Stalled = 0; return false; // Caller should retry. Never mind about _contentions as this monitor has been deflated. } @@ -426,65 +428,83 @@ return -1; } -// Install the displaced markword of a deflated monitor into the object -// associated with the monitor. -// This method is idempotent and is executed by both mutators wanting to -// acquire a monitor for an object and the thread deflating monitors. -// A mutator trying to install a hash in the monitor's _header field can -// also run in parallel to this method. -void ObjectMonitor::install_displaced_markword_in_object() { +// Install the displaced mark word (dmw) of a deflating ObjectMonitor +// into the header of the object associated with the monitor. This +// idempotent method is called by a thread that is deflating a +// monitor and by other threads that have detected a race with the +// deflation process. +void ObjectMonitor::install_displaced_markword_in_object(const oop obj) { + // This function must only be called when (owner == DEFLATER_MARKER + // && contentions <= 0), but we can't guarantee that here because + // those values could change when the ObjectMonitor gets moved from + // the global free list to a per-thread free list. + + guarantee(obj != NULL, "must be non-NULL"); + if (object() != obj) { + // ObjectMonitor's object ref no longer refers to the target object + // so the object's header has already been restored. + return; + } + markOop dmw = header(); if (dmw == NULL) { - // The thread deflating monitors has won the race so we - // have nothing to do. + // ObjectMonitor's header/dmw has been cleared by the deflating + // thread so the object's header has already been restored. return; } - // A non-NULL dmw has to be either neutral or is participating in - // this restoration protocol. + // A non-NULL dmw has to be either neutral (not locked and not marked) + // or is already participating in this restoration protocol. assert(dmw->is_neutral() || (dmw->is_marked() && dmw->hash() == 0), - "failed precondition: is_neutral=%d, is_marked=%d, hash=" - INTPTR_FORMAT, dmw->is_neutral(), dmw->is_marked(), dmw->hash()); + "failed precondition: dmw=" INTPTR_FORMAT, p2i(dmw)); + markOop marked_dmw = NULL; if (!dmw->is_marked() && dmw->hash() == 0) { - // This dmw is neutral and has not yet started the restoration - // protocol so we mark a copy of the dmw to begin the protocol. - markOop marked_dmw = dmw->set_marked(); - assert(marked_dmw->is_marked() && marked_dmw->hash() == 0, - "sanity_check: is_marked=%d, hash=" INTPTR_FORMAT, - marked_dmw->is_marked(), marked_dmw->hash()); - - // There can be three different racers trying to update the _header - // field and the return dmw value will tell us what cleanup needs - // to be done (if any) after the race winner: - // 1) A mutator trying to install a hash in the object. - // Note: That mutator is not executing this code, but it is - // trying to update the _header field. - // If winner: dmw will contain the hash and be unmarked - // 2a) A mutator trying to acquire the monitor via enter(): - // If winner: dmw is marked and hash() == 0 - // 2b) The thread deflating the monitor via deflate_monitor_using_JT(): - // If winner: dmw is marked and hash() == 0 + // This dmw has not yet started the restoration protocol so we + // mark a copy of the dmw to begin the protocol. + // Note: A dmw with a hashcode does not take this code path. + marked_dmw = dmw->set_marked(); + + // All of the callers to this function can be racing with each + // other trying to update the _header field. dmw = (markOop) Atomic::cmpxchg(marked_dmw, &_header, dmw); + if (dmw == NULL) { + // ObjectMonitor's header/dmw has been cleared by the deflating + // thread so the object's header has already been restored. + return; + } + // The _header field is now marked. The winner's 'dmw' variable + // contains the original, unmarked header/dmw value and any + // losers have a marked header/dmw value that will be cleaned + // up below. } if (dmw->is_marked()) { - // The dmw copy is marked which means a hash was not set by a racing - // thread. Clear the mark from the copy in preparation for possible - // restoration from this thread. - assert(dmw->hash() == 0, "must be 0: hash=" INTPTR_FORMAT, dmw->hash()); + // Clear the mark from the header/dmw copy in preparation for + // possible restoration from this thread. + assert(dmw->hash() == 0, "hashcode must be 0: dmw=" INTPTR_FORMAT, + p2i(dmw)); dmw = dmw->set_unmarked(); } - assert(dmw->is_neutral(), "must be a neutral markword"); + assert(dmw->is_neutral(), "must be neutral: dmw=" INTPTR_FORMAT, p2i(dmw)); - oop const obj = (oop) object(); - // Install displaced markword if object markword still points to this - // monitor. Both the mutator trying to enter() and the thread deflating - // the monitor will reach this point, but only one can win. - // Note: If a mutator won the cmpxchg() race above and installed a hash - // in _header, then the updated dmw contains that hash and we'll install - // it in the object's markword here. + // Install displaced mark word if the object's header still points + // to this ObjectMonitor. All racing callers to this function will + // reach this point, but only one can win. obj->cas_set_mark(dmw, markOopDesc::encode(this)); + + // Note: It does not matter which thread restored the header/dmw + // into the object's header. The thread deflating the monitor just + // wanted the object's header restored and it is. The threads that + // detected a race with the deflation process also wanted the + // object's header restored before they retry their operation and + // because it is restored they will only retry once. + + if (marked_dmw != NULL) { + // Clear _header to NULL if it is still marked_dmw so a racing + // install_displaced_markword_in_object() can bail out sooner. + Atomic::cmpxchg((markOop)NULL, &_header, marked_dmw); + } } #define MAX_RECHECK_INTERVAL 1000 @@ -503,12 +523,15 @@ } if (_owner == DEFLATER_MARKER) { - guarantee(0 < _contentions, "_owner == DEFLATER_MARKER && _contentions <= 0 should have been handled by the caller"); - // Deflater thread tried to lock this monitor, but it failed to make _contentions negative and gave up. - // Try to acquire monitor. + // The deflation protocol finished the first part (setting _owner), but + // it failed the second part (making _contentions negative) and bailed. + // Because we're called from enter() we have at least one contention. + guarantee(_contentions > 0, "owner == DEFLATER_MARKER && contentions <= 0 " + "should have been handled by the caller: contentions=%d", + _contentions); if (Atomic::cmpxchg(Self, &_owner, DEFLATER_MARKER) == DEFLATER_MARKER) { + // Acquired the monitor. assert(_succ != Self, "invariant"); - assert(_owner == Self, "invariant"); assert(_Responsible != Self, "invariant"); return; } @@ -631,8 +654,12 @@ if (TryLock(Self) > 0) break; if (_owner == DEFLATER_MARKER) { - guarantee(0 < _contentions, "_owner == DEFLATER_MARKER && _contentions <= 0 should have been handled by the caller"); - // Deflater thread tried to lock this monitor, but it failed to make _contentions negative and gave up. + // The deflation protocol finished the first part (setting _owner), but + // it failed the second part (making _contentions negative) and bailed. + // Because we're called from enter() we have at least one contention. + guarantee(_contentions > 0, "owner == DEFLATER_MARKER && contentions <= 0 " + "should have been handled by the caller: contentions=%d", + _contentions); if (Atomic::cmpxchg(Self, &_owner, DEFLATER_MARKER) == DEFLATER_MARKER) { // Acquired the monitor. break; @@ -761,7 +788,12 @@ if (TrySpin(Self) > 0) break; if (_owner == DEFLATER_MARKER) { - guarantee(0 <= _contentions, "Impossible: _owner == DEFLATER_MARKER && _contentions < 0, monitor must not be owned by deflater thread here"); + // The deflation protocol finished the first part (setting _owner), + // but it will observe _waiters != 0 and will bail out. Because we're + // called from wait() we may or may not have any contentions. + guarantee(_contentions >= 0, "owner == DEFLATER_MARKER && contentions < 0 " + "should have been handled by the caller: contentions=%d", + _contentions); if (Atomic::cmpxchg(Self, &_owner, DEFLATER_MARKER) == DEFLATER_MARKER) { // Acquired the monitor. break; @@ -975,7 +1007,8 @@ // way we should encounter this situation is in the presence of // unbalanced JNI locking. TODO: CheckJNICalls. // See also: CR4414101 - assert(false, "Non-balanced monitor enter/exit! Likely JNI locking"); + assert(false, "Non-balanced monitor enter/exit! Likely JNI locking: " + "owner=" INTPTR_FORMAT, p2i(_owner)); return; } } @@ -1467,7 +1500,7 @@ ObjectWaiter::TStates v = node.TState; if (v == ObjectWaiter::TS_RUN) { const bool success = enter(Self); - guarantee(success, "enter signaled for a retry, but monitor should not have been deflated as waiters > 0"); + ADIM_guarantee(success, "enter signaled for a retry, but monitor should not have been deflated as waiters > 0"); } else { guarantee(v == ObjectWaiter::TS_ENTER || v == ObjectWaiter::TS_CXQ, "invariant"); ReenterI(Self, &node); @@ -2056,7 +2089,7 @@ // bool ObjectMonitorHandle::save_om_ptr(oop object, markOop mark) { guarantee(mark->has_monitor(), "sanity check: mark=" INTPTR_FORMAT, - p2i((address)mark)); + p2i(mark)); ObjectMonitor * om_ptr = mark->monitor(); om_ptr->inc_ref_count(); @@ -2065,8 +2098,11 @@ // Race here if monitor is not owned! The above ref_count bump // will cause subsequent async deflation to skip it. However, // previous or concurrent async deflation is a race. - if (om_ptr->_owner == DEFLATER_MARKER) { - // Async deflation won the race so we have to retry. + if (om_ptr->_owner == DEFLATER_MARKER && om_ptr->_contentions <= 0) { + // Async deflation is in progress. Attempt to restore the + // header/dmw to the object's header so that we only retry once + // if the deflater thread happens to be slow. + om_ptr->install_displaced_markword_in_object(object); om_ptr->dec_ref_count(); return false; } @@ -2076,6 +2112,7 @@ const markOop tmp = object->mark(); if (!tmp->has_monitor() || tmp->monitor() != om_ptr) { // Async deflation and reuse won the race so we have to retry. + // Skip object header restoration since that's already done. om_ptr->dec_ref_count(); return false; } @@ -2089,11 +2126,13 @@ // For internal use by ObjectSynchronizer::inflate(). void ObjectMonitorHandle::set_om_ptr(ObjectMonitor * om_ptr) { - // Cannot guarantee() is_new() here. As soon as the ObjectMonitor* - // is attached to the object in inflate(), it can be used by other - // JavaThreads. - // guarantee(om_ptr->is_new(), "sanity check: allocation_state=%d", - // int(om_ptr->allocation_state())); - om_ptr->inc_ref_count(); - _om_ptr = om_ptr; + if (_om_ptr == NULL) { + guarantee(om_ptr != NULL, "cannot clear an unset om_ptr"); + om_ptr->inc_ref_count(); + _om_ptr = om_ptr; + } else { + guarantee(om_ptr == NULL, "can only clear a set om_ptr"); + _om_ptr->dec_ref_count(); + _om_ptr = NULL; + } } --- old/src/hotspot/share/runtime/objectMonitor.hpp 2019-04-18 22:07:22.696307022 -0400 +++ new/src/hotspot/share/runtime/objectMonitor.hpp 2019-04-18 22:07:21.892307035 -0400 @@ -150,7 +150,7 @@ sizeof(volatile markOop) + sizeof(void * volatile) + sizeof(ObjectMonitor *)); protected: // protected for JvmtiRawMonitor - // Used by async monitor deflation as a marker in the _owner field: + // Used by async deflation as a marker in the _owner field: #define DEFLATER_MARKER reinterpret_cast(-1) void * volatile _owner; // pointer to owning thread OR BasicLock volatile jlong _previous_owner_tid; // thread id of the previous owner of the monitor @@ -349,7 +349,7 @@ int TrySpin(Thread * Self); void ExitEpilog(Thread * Self, ObjectWaiter * Wakee); bool ExitSuspendEquivalent(JavaThread * Self); - void install_displaced_markword_in_object(); + void install_displaced_markword_in_object(const oop obj); }; // A helper object for managing an ObjectMonitor*'s ref_count. There @@ -372,4 +372,15 @@ void set_om_ptr(ObjectMonitor * om_ptr); }; +// Macro to use guarantee() for more strict AsyncDeflateIdleMonitors +// checks and assert() otherwise. +#define ADIM_guarantee(p, ...) \ + do { \ + if (AsyncDeflateIdleMonitors) { \ + guarantee(p, __VA_ARGS__); \ + } else { \ + assert(p, __VA_ARGS__); \ + } \ + } while (0) + #endif // SHARE_RUNTIME_OBJECTMONITOR_HPP --- old/src/hotspot/share/runtime/objectMonitor.inline.hpp 2019-04-18 22:07:24.016306999 -0400 +++ new/src/hotspot/share/runtime/objectMonitor.inline.hpp 2019-04-18 22:07:23.336307010 -0400 @@ -56,20 +56,33 @@ } inline void ObjectMonitor::clear() { + assert(_header != NULL, "must be non-NULL"); assert(_contentions == 0, "must be 0: contentions=%d", _contentions); assert(_owner == NULL, "must be NULL: owner=" INTPTR_FORMAT, p2i(_owner)); + _header = NULL; + clear_using_JT(); } inline void ObjectMonitor::clear_using_JT() { - // When clearing using a JavaThread, we leave _owner == DEFLATER_MARKER - // and _contentions < 0 to force any racing threads to retry. Unlike other - // *_using_JT() functions, we cannot assert AsyncDeflateIdleMonitors - // or Thread::current()->is_Java_thread() because clear() calls this - // function for the rest of its checks. - - assert(_header != NULL, "must be non-NULL"); + // Unlike other *_using_JT() functions, we cannot assert + // AsyncDeflateIdleMonitors or Thread::current()->is_Java_thread() + // because clear() calls this function for the rest of its checks. + + if (AsyncDeflateIdleMonitors) { + // Async deflation protocol uses the _header, _contentions and _owner + // fields. While the ObjectMonitor being deflated is on the global free + // list, we leave those three fields alone; _owner == DEFLATER_MARKER + // and _contentions < 0 will force any racing threads to retry. The + // _header field is used by install_displaced_markword_in_object() + // in the last part of the deflation protocol so we cannot check + // its values here. + guarantee(_owner == NULL || _owner == DEFLATER_MARKER, + "must be NULL or DEFLATER_MARKER: owner=" INTPTR_FORMAT, + p2i(_owner)); + guarantee(_contentions <= 0, "must be <= 0: contentions=%d", _contentions); + } assert(_waiters == 0, "must be 0: waiters=%d", _waiters); assert(_recursions == 0, "must be 0: recursions=" INTPTR_FORMAT, _recursions); assert(_object != NULL, "must be non-NULL"); @@ -78,7 +91,6 @@ // decrement _ref_count. set_allocation_state(Free); - _header = NULL; _object = NULL; // Do not clear _ref_count here because _ref_count is for indicating // that the ObjectMonitor* is in use which is orthogonal to whether @@ -148,30 +160,18 @@ } inline void ObjectMonitor::dec_ref_count() { - // The decrement needs to be MO_ACQ_REL. At the moment, the Atomic::dec - // backend on PPC does not yet conform to these requirements. Therefore - // the decrement is simulated with an Atomic::sub(1, &addr). Without - // this MO_ACQ_REL Atomic::dec simulation, AsyncDeflateIdleMonitors is - // not safe. - Atomic::sub((jint)1, &_ref_count); + // The decrement only needs to be MO_ACQ_REL since the reference + // counter is volatile. + Atomic::dec(&_ref_count); guarantee(_ref_count >= 0, "sanity check: ref_count=%d", _ref_count); } inline void ObjectMonitor::inc_ref_count() { - // The increment needs to be MO_SEQ_CST. At the moment, the Atomic::inc - // backend on PPC does not yet conform to these requirements. Therefore - // the increment is simulated with a load phi; cas phi + 1; loop. - // Without this MO_SEQ_CST Atomic::inc simulation, AsyncDeflateIdleMonitors - // is not safe. - for (;;) { - jint sample = OrderAccess::load_acquire(&_ref_count); - guarantee(sample >= 0, "sanity check: sample=%d", (int)sample); - if (Atomic::cmpxchg(sample + 1, &_ref_count, sample) == sample) { - // Incremented _ref_count without interference. - return; - } - // Implied else: Saw interference so loop and try again. - } + // The increment needs to be MO_SEQ_CST so that the reference + // counter update is seen as soon as possible in a race with the + // async deflation protocol. + Atomic::inc(&_ref_count); + guarantee(_ref_count > 0, "sanity check: ref_count=%d", _ref_count); } inline jint ObjectMonitor::ref_count() const { --- old/src/hotspot/share/runtime/safepoint.cpp 2019-04-18 22:07:25.364306975 -0400 +++ new/src/hotspot/share/runtime/safepoint.cpp 2019-04-18 22:07:24.620306988 -0400 @@ -525,6 +525,10 @@ _counters(counters) {} void do_thread(Thread* thread) { + // deflate_thread_local_monitors() handles or requests deflation of + // this thread's idle monitors. If !AsyncDeflateIdleMonitors or if + // there is a special cleanup request, deflation is handled now. + // Otherwise, async deflation is requested via a flag. ObjectSynchronizer::deflate_thread_local_monitors(thread, _counters); if (_nmethod_cl != NULL && thread->is_Java_thread() && ! thread->is_Code_cache_sweeper_thread()) { @@ -561,7 +565,7 @@ // when a special cleanup has been requested. // Note: This logging output will include global idle monitor // elapsed times, but not global idle monitor deflation count. - ObjectSynchronizer::do_safepoint_work(!AsyncDeflateIdleMonitors ? _counters : NULL); + ObjectSynchronizer::do_safepoint_work(_counters); post_safepoint_cleanup_task_event(event, safepoint_id, name); } --- old/src/hotspot/share/runtime/synchronizer.cpp 2019-04-18 22:07:26.844306950 -0400 +++ new/src/hotspot/share/runtime/synchronizer.cpp 2019-04-18 22:07:26.048306963 -0400 @@ -748,86 +748,92 @@ assert(Universe::verify_in_progress() || DumpSharedSpaces || ((JavaThread *)Self)->thread_state() != _thread_blocked, "invariant"); - Retry: - ObjectMonitor* monitor = NULL; - markOop temp, test; - intptr_t hash; - markOop mark = ReadStableMark(obj); - - // object should remain ineligible for biased locking - assert(!mark->has_bias_pattern(), "invariant"); - - if (mark->is_neutral()) { - hash = mark->hash(); // this is a normal header - if (hash != 0) { // if it has hash, just return it - return hash; - } - hash = get_next_hash(Self, obj); // allocate a new hash code - temp = mark->copy_set_hash(hash); // merge the hash code into header - // use (machine word version) atomic operation to install the hash - test = obj->cas_set_mark(temp, mark); - if (test == mark) { - return hash; - } - // If atomic operation failed, we must inflate the header - // into heavy weight monitor. We could add more code here - // for fast path, but it does not worth the complexity. - } else if (mark->has_monitor()) { - ObjectMonitorHandle omh; - if (!omh.save_om_ptr(obj, mark)) { - // Lost a race with async deflation so try again. - assert(AsyncDeflateIdleMonitors, "sanity check"); - goto Retry; + while (true) { + ObjectMonitor* monitor = NULL; + markOop temp, test; + intptr_t hash; + markOop mark = ReadStableMark(obj); + + // object should remain ineligible for biased locking + assert(!mark->has_bias_pattern(), "invariant"); + + if (mark->is_neutral()) { + hash = mark->hash(); // this is a normal header + if (hash != 0) { // if it has hash, just return it + return hash; + } + hash = get_next_hash(Self, obj); // allocate a new hash code + temp = mark->copy_set_hash(hash); // merge the hash code into header + // use (machine word version) atomic operation to install the hash + test = obj->cas_set_mark(temp, mark); + if (test == mark) { + return hash; + } + // If atomic operation failed, we must inflate the header + // into heavy weight monitor. We could add more code here + // for fast path, but it does not worth the complexity. + } else if (mark->has_monitor()) { + ObjectMonitorHandle omh; + if (!omh.save_om_ptr(obj, mark)) { + // Lost a race with async deflation so try again. + assert(AsyncDeflateIdleMonitors, "sanity check"); + continue; + } + monitor = omh.om_ptr(); + temp = monitor->header(); + assert(temp->is_neutral(), "invariant: header=" INTPTR_FORMAT, p2i(temp)); + hash = temp->hash(); + if (hash != 0) { + return hash; + } + // Skip to the following code to reduce code size + } else if (Self->is_lock_owned((address)mark->locker())) { + temp = mark->displaced_mark_helper(); // this is a lightweight monitor owned + assert(temp->is_neutral(), "invariant: header=" INTPTR_FORMAT, p2i(temp)); + hash = temp->hash(); // by current thread, check if the displaced + if (hash != 0) { // header contains hash code + return hash; + } + // WARNING: + // The displaced header in the BasicLock on a thread's stack + // is strictly immutable. It CANNOT be changed in ANY cases. + // So we have to inflate the stack lock into an ObjectMonitor + // even if the current thread owns the lock. The BasicLock on + // a thread's stack can be asynchronously read by other threads + // during an inflate() call so any change to that stack memory + // may not propagate to other threads correctly. } + + // Inflate the monitor to set hash code + ObjectMonitorHandle omh; + inflate(&omh, Self, obj, inflate_cause_hash_code); monitor = omh.om_ptr(); - temp = monitor->header(); - assert(temp->is_neutral(), "invariant: header=" INTPTR_FORMAT, p2i(temp)); - hash = temp->hash(); - if (hash != 0) { - return hash; - } - // Skip to the following code to reduce code size - } else if (Self->is_lock_owned((address)mark->locker())) { - temp = mark->displaced_mark_helper(); // this is a lightweight monitor owned - assert(temp->is_neutral(), "invariant: header=" INTPTR_FORMAT, p2i(temp)); - hash = temp->hash(); // by current thread, check if the displaced - if (hash != 0) { // header contains hash code - return hash; - } - // WARNING: - // The displaced header in the BasicLock on a thread's stack - // is strictly immutable. It CANNOT be changed in ANY cases. - // So we have to inflate the stack lock into an ObjectMonitor - // even if the current thread owns the lock. The BasicLock on - // a thread's stack can be asynchronously read by other threads - // during an inflate() call so any change to that stack memory - // may not propagate to other threads correctly. - } - - // Inflate the monitor to set hash code - ObjectMonitorHandle omh; - inflate(&omh, Self, obj, inflate_cause_hash_code); - monitor = omh.om_ptr(); - // Load displaced header and check it has hash code - mark = monitor->header(); - assert(mark->is_neutral(), "invariant: header=" INTPTR_FORMAT, p2i(mark)); - hash = mark->hash(); - if (hash == 0) { - hash = get_next_hash(Self, obj); - temp = mark->copy_set_hash(hash); // merge hash code into header - assert(temp->is_neutral(), "invariant: header=" INTPTR_FORMAT, p2i(temp)); - test = Atomic::cmpxchg(temp, monitor->header_addr(), mark); - if (test != mark) { - // The only update to the ObjectMonitor's header/dmw field - // is to merge in the hash code. If someone adds a new usage - // of the header/dmw field, please update this code. - hash = test->hash(); - assert(test->is_neutral(), "invariant: header=" INTPTR_FORMAT, p2i(test)); - assert(hash != 0, "Trivial unexpected object/monitor header usage."); + // Load displaced header and check it has hash code + mark = monitor->header(); + assert(mark->is_neutral(), "invariant: header=" INTPTR_FORMAT, p2i(mark)); + hash = mark->hash(); + if (hash == 0) { + hash = get_next_hash(Self, obj); + temp = mark->copy_set_hash(hash); // merge hash code into header + assert(temp->is_neutral(), "invariant: header=" INTPTR_FORMAT, p2i(temp)); + test = Atomic::cmpxchg(temp, monitor->header_addr(), mark); + if (test != mark) { + // The only non-deflation update to the ObjectMonitor's + // header/dmw field is to merge in the hash code. If someone + // adds a new usage of the header/dmw field, please update + // this code. + // ObjectMonitor::install_displaced_markword_in_object() + // does mark the header/dmw field as part of async deflation, + // but that protocol cannot happen now due to the + // ObjectMonitorHandle above. + hash = test->hash(); + assert(test->is_neutral(), "invariant: header=" INTPTR_FORMAT, p2i(test)); + assert(hash != 0, "Trivial unexpected object/monitor header usage."); + } } + // We finally get the hash + return hash; } - // We finally get the hash - return hash; } // Deprecated -- use FastHashCode() instead. @@ -1165,6 +1171,8 @@ gFreeList = take->FreeNext; guarantee(take->object() == NULL, "invariant"); if (AsyncDeflateIdleMonitors) { + // Clear any values we allowed to linger during async deflation. + take->_header = NULL; take->set_owner(NULL); take->_contentions = 0; } @@ -1344,7 +1352,7 @@ s->set_owner(NULL); // redundant but good hygiene } guarantee(tail != NULL, "invariant"); - guarantee(Self->omFreeCount == tally, "free-count off"); + ADIM_guarantee(Self->omFreeCount == tally, "free-count off"); Self->omFreeList = NULL; Self->omFreeCount = 0; } @@ -1361,10 +1369,10 @@ for (cur_om = inUseList; cur_om != NULL; cur_om = cur_om->FreeNext) { inUseTail = cur_om; inUseTally++; - guarantee(cur_om->is_active(), "invariant"); + ADIM_guarantee(cur_om->is_active(), "invariant"); } guarantee(inUseTail != NULL, "invariant"); - guarantee(Self->omInUseCount == inUseTally, "in-use count off"); + ADIM_guarantee(Self->omInUseCount == inUseTally, "in-use count off"); Self->omInUseList = NULL; Self->omInUseCount = 0; } @@ -1559,7 +1567,7 @@ markOop dmw = mark->displaced_mark_helper(); // Catch if the object's header is not neutral (not locked and // not marked is what we care about here). - assert(dmw->is_neutral(), "invariant: header=" INTPTR_FORMAT, p2i(dmw)); + ADIM_guarantee(dmw->is_neutral(), "invariant: header=" INTPTR_FORMAT, p2i(dmw)); // Setup monitor fields to proper values -- prepare the monitor m->set_header(dmw); @@ -1573,6 +1581,10 @@ m->set_object(object); // TODO-FIXME: assert BasicLock->dhw != 0. + omh_p->set_om_ptr(m); + assert(m->is_new(), "freshly allocated monitor must be new"); + m->set_allocation_state(ObjectMonitor::Old); + // Must preserve store ordering. The monitor state must // be stable at the time of publishing the monitor address. guarantee(object->mark() == markOopDesc::INFLATING(), "invariant"); @@ -1590,8 +1602,7 @@ if (event.should_commit()) { post_monitor_inflate_event(&event, object, cause); } - assert(!m->is_free(), "post-condition"); - omh_p->set_om_ptr(m); + ADIM_guarantee(!m->is_free(), "inflated monitor to be returned cannot be free"); return; } @@ -1607,7 +1618,7 @@ // Catch if the object's header is not neutral (not locked and // not marked is what we care about here). - assert(mark->is_neutral(), "invariant: header=" INTPTR_FORMAT, p2i(mark)); + ADIM_guarantee(mark->is_neutral(), "invariant: header=" INTPTR_FORMAT, p2i(mark)); ObjectMonitor * m; if (!AsyncDeflateIdleMonitors || cause == inflate_cause_vm_internal) { // If !AsyncDeflateIdleMonitors or if an internal inflation, then @@ -1629,10 +1640,16 @@ m->_Responsible = NULL; m->_SpinDuration = ObjectMonitor::Knob_SpinLimit; // consider: keep metastats by type/class + omh_p->set_om_ptr(m); + assert(m->is_new(), "freshly allocated monitor must be new"); + m->set_allocation_state(ObjectMonitor::Old); + if (object->cas_set_mark(markOopDesc::encode(m), mark) != mark) { m->set_header(NULL); m->set_object(NULL); m->Recycle(); + omh_p->set_om_ptr(NULL); + // omRelease() will reset the allocation state omRelease(Self, m, true); m = NULL; continue; @@ -1653,7 +1670,7 @@ if (event.should_commit()) { post_monitor_inflate_event(&event, object, cause); } - omh_p->set_om_ptr(m); + ADIM_guarantee(!m->is_free(), "inflated monitor to be returned cannot be free"); return; } } @@ -1681,27 +1698,23 @@ // This is an unfortunate aspect of this design. void ObjectSynchronizer::do_safepoint_work(DeflateMonitorCounters* _counters) { - if (!AsyncDeflateIdleMonitors) { - // Use the older mechanism for the global in-use list. + assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); + + // The per-thread in-use lists are handled in + // ParallelSPCleanupThreadClosure::do_thread(). + + if (!AsyncDeflateIdleMonitors || is_cleanup_requested()) { + // Use the older mechanism for the global in-use list or + // if a special cleanup has been requested. ObjectSynchronizer::deflate_idle_monitors(_counters); return; } - assert(_counters == NULL, "not used with AsyncDeflateIdleMonitors"); - log_debug(monitorinflation)("requesting deflation of idle monitors."); // Request deflation of global idle monitors by the ServiceThread: _gOmShouldDeflateIdleMonitors = true; MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag); Service_lock->notify_all(); - - // Request deflation of per-thread idle monitors by each JavaThread: - for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) { - if (jt->omInUseCount > 0) { - // This JavaThread is using monitors so check it. - jt->omShouldDeflateIdleMonitors = true; - } - } } // Deflate a single monitor if not in-use @@ -1759,14 +1772,14 @@ // Deflate the specified ObjectMonitor if not in-use using a JavaThread. // Returns true if it was deflated and false otherwise. // -// The async deflation protocol sets _owner to DEFLATER_MARKER and -// makes _contentions negative as signals to contending threads that +// The async deflation protocol sets owner to DEFLATER_MARKER and +// makes contentions negative as signals to contending threads that // an async deflation is in progress. There are a number of checks // as part of the protocol to make sure that the calling thread has // not lost the race to a contending thread. // // The ObjectMonitor has been successfully async deflated when: -// (_owner == DEFLATER_MARKER && _contentions < 0). Contending threads +// (owner == DEFLATER_MARKER && contentions < 0). Contending threads // that see those values know to retry their operation. // bool ObjectSynchronizer::deflate_monitor_using_JT(ObjectMonitor* mid, @@ -1776,7 +1789,8 @@ assert(Thread::current()->is_Java_thread(), "precondition"); // A newly allocated ObjectMonitor should not be seen here so we // avoid an endless inflate/deflate cycle. - assert(mid->is_old(), "precondition"); + assert(mid->is_old(), "must be old: allocation_state=%d", + (int) mid->allocation_state()); if (mid->is_busy() || mid->ref_count() != 0) { // Easy checks are first - the ObjectMonitor is busy or ObjectMonitor* @@ -1784,9 +1798,9 @@ return false; } - if (Atomic::cmpxchg(DEFLATER_MARKER, &mid->_owner, (void*)NULL) == NULL) { + if (Atomic::replace_if_null(DEFLATER_MARKER, &(mid->_owner))) { // ObjectMonitor is not owned by another thread. Our setting - // _owner to DEFLATER_MARKER forces any contending thread through + // owner to DEFLATER_MARKER forces any contending thread through // the slow path. This is just the first part of the async // deflation dance. @@ -1795,48 +1809,55 @@ // mid->is_busy() above and has already waited on it which // makes it busy so no deflation. Or the ObjectMonitor* is // in use for some other operation like inflate(). Restore - // _owner to NULL if it is still DEFLATER_MARKER. + // owner to NULL if it is still DEFLATER_MARKER. Atomic::cmpxchg((void*)NULL, &mid->_owner, DEFLATER_MARKER); return false; } if (Atomic::cmpxchg(-max_jint, &mid->_contentions, (jint)0) == 0) { - // Make _contentions negative to force racing threads to retry. + // Make contentions negative to force racing threads to retry. // This is the second part of the async deflation dance. - if (mid->_owner == DEFLATER_MARKER) { - // If _owner is still DEFLATER_MARKER, then we have successfully + if (mid->_owner == DEFLATER_MARKER && mid->ref_count() == 0) { + // If owner is still DEFLATER_MARKER, then we have successfully // signaled any racing threads to retry. If it is not, then we - // have lost the race to another thread and the ObjectMonitor is - // now busy. This is the third and final part of the async + // have lost the race to an entering thread and the ObjectMonitor + // is now busy. If the ObjectMonitor* is in use, then we have + // lost that race. This is the third and final part of the async // deflation dance. - // Note: This _owner check solves the ABA problem with _contentions + // Note: This owner check solves the ABA problem with contentions // where another thread acquired the ObjectMonitor, finished - // using it and restored the _contentions to zero. + // using it and restored the contentions to zero. + // Note: This ref_count check solves the race with save_om_ptr() + // where its ref_count increment happens after the first ref_count + // check in this function and before contentions is made negative. // Sanity checks for the races: - guarantee(mid->_waiters == 0, "should be no waiters"); - guarantee(mid->_cxq == NULL, "should be no contending threads"); - guarantee(mid->_EntryList == NULL, "should be no entering threads"); + guarantee(mid->_waiters == 0, "must be 0: waiters=%d", mid->_waiters); + guarantee(mid->_cxq == NULL, "must be no contending threads: cxq=" + INTPTR_FORMAT, p2i(mid->_cxq)); + guarantee(mid->_EntryList == NULL, + "must be no entering threads: EntryList=" INTPTR_FORMAT, + p2i(mid->_EntryList)); + const oop obj = (oop) mid->object(); if (log_is_enabled(Trace, monitorinflation)) { - oop obj = (oop) mid->object(); - assert(obj != NULL, "sanity check"); - if (obj->is_instance()) { - ResourceMark rm; - log_trace(monitorinflation)("deflate_monitor_using_JT: " - "object=" INTPTR_FORMAT ", mark=" INTPTR_FORMAT ", type='%s'", - p2i(obj), p2i(obj->mark()), - obj->klass()->external_name()); - } + ResourceMark rm; + log_trace(monitorinflation)("deflate_monitor_using_JT: " + "object=" INTPTR_FORMAT ", mark=" + INTPTR_FORMAT ", type='%s'", + p2i(obj), p2i(obj->mark()), + obj->klass()->external_name()); } // Install the old mark word if nobody else has already done it. - mid->install_displaced_markword_in_object(); + mid->install_displaced_markword_in_object(obj); mid->clear_using_JT(); - assert(mid->object() == NULL, "invariant"); - assert(mid->is_free(), "invariant"); + assert(mid->object() == NULL, "must be NULL: object=" INTPTR_FORMAT, + p2i(mid->object())); + assert(mid->is_free(), "must be free: allocation_state=%d", + (int) mid->allocation_state()); // Move the deflated ObjectMonitor to the working free list // defined by freeHeadp and freeTailp. @@ -1848,7 +1869,9 @@ // We append to the list so the caller can use mid->FreeNext // to fix the linkages in its context. ObjectMonitor * prevtail = *freeTailp; - assert(prevtail->FreeNext == NULL, "not cleaned up by the caller"); + // Should have been cleaned up by the caller: + assert(prevtail->FreeNext == NULL, "must be NULL: FreeNext=" + INTPTR_FORMAT, p2i(prevtail->FreeNext)); prevtail->FreeNext = mid; } *freeTailp = mid; @@ -1858,26 +1881,32 @@ // refers to this ObjectMonitor. Those linkages have to be // cleaned up by the caller who has the complete context. - // We leave _owner == DEFLATER_MARKER and _contentions < 0 + // We leave owner == DEFLATER_MARKER and contentions < 0 // to force any racing threads to retry. return true; // Success, ObjectMonitor has been deflated. } - // The _owner was changed from DEFLATER_MARKER so we lost the - // race since the ObjectMonitor is now busy. Add back max_jint - // to restore the _contentions field to its proper value (which - // may not be what we saw above). + // The owner was changed from DEFLATER_MARKER or ObjectMonitor* + // is in use so we lost the race since the ObjectMonitor is now + // busy. + + // Restore owner to NULL if it is still DEFLATER_MARKER: + Atomic::cmpxchg((void*)NULL, &mid->_owner, DEFLATER_MARKER); + + // Add back max_jint to restore the contentions field to its + // proper value (which may not be what we saw above): Atomic::add(max_jint, &mid->_contentions); - assert(mid->_contentions >= 0, "_contentions should not be negative"); + assert(mid->_contentions >= 0, "must not be negative: contentions=%d", + mid->_contentions); } - // The _contentions was no longer 0 so we lost the race since the + // The contentions was no longer 0 so we lost the race since the // ObjectMonitor is now busy. - assert(mid->_owner != DEFLATER_MARKER, "should no longer be set"); + assert(mid->_owner != DEFLATER_MARKER, "must not be DEFLATER_MARKER"); } - // The _owner field is no longer NULL so we lost the race since the + // The owner field is no longer NULL so we lost the race since the // ObjectMonitor is now busy. return false; } @@ -1974,18 +2003,13 @@ } else { // mid is considered in-use if it does not have an associated // Java object or mid is not old or deflation did not succeed. - // A mid->is_new() node can be seen here when it is freshly returned - // by omAlloc() (and skips the deflation code path). + // A mid->is_new() node can be seen here when it is freshly + // returned by omAlloc() (and skips the deflation code path). // A mid->is_old() node can be seen here when deflation failed. // A mid->is_free() node can be seen here when a fresh node from // omAlloc() is released by omRelease() due to losing the race // in inflate(). - if (mid->object() != NULL && mid->is_new()) { - // mid has an associated Java object and has now been seen - // as newly allocated so mark it as "old". - mid->set_allocation_state(ObjectMonitor::Old); - } cur_mid_in_use = mid; mid = mid->FreeNext; @@ -2015,8 +2039,16 @@ } void ObjectSynchronizer::deflate_idle_monitors(DeflateMonitorCounters* counters) { - assert(!AsyncDeflateIdleMonitors, "sanity check"); assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); + + if (AsyncDeflateIdleMonitors) { + // Nothing to do when global idle ObjectMonitors are deflated using + // a JavaThread unless a special cleanup has been requested. + if (!is_cleanup_requested()) { + return; + } + } + bool deflated = false; ObjectMonitor * freeHeadp = NULL; // Local SLL of scavenged monitors @@ -2074,72 +2106,11 @@ void ObjectSynchronizer::deflate_global_idle_monitors_using_JT() { assert(AsyncDeflateIdleMonitors, "sanity check"); assert(Thread::current()->is_Java_thread(), "precondition"); - JavaThread * cur_jt = JavaThread::current(); + JavaThread * self = JavaThread::current(); _gOmShouldDeflateIdleMonitors = false; - int deflated_count = 0; - ObjectMonitor * freeHeadp = NULL; // Local SLL of scavenged ObjectMonitors - ObjectMonitor * freeTailp = NULL; - ObjectMonitor * savedMidInUsep = NULL; - elapsedTimer timer; - - if (log_is_enabled(Info, monitorinflation)) { - timer.start(); - } - Thread::muxAcquire(&gListLock, "deflate_global_idle_monitors_using_JT(1)"); - OM_PERFDATA_OP(MonExtant, set_value(gOmInUseCount)); - - do { - int local_deflated_count = deflate_monitor_list_using_JT((ObjectMonitor **)&gOmInUseList, &freeHeadp, &freeTailp, &savedMidInUsep); - gOmInUseCount -= local_deflated_count; - deflated_count += local_deflated_count; - - if (freeHeadp != NULL) { - // Move the scavenged ObjectMonitors to the global free list. - guarantee(freeTailp != NULL && local_deflated_count > 0, "freeTailp=" INTPTR_FORMAT ", local_deflated_count=%d", p2i(freeTailp), local_deflated_count); - assert(freeTailp->FreeNext == NULL, "invariant"); - - // Constant-time list splice - prepend scavenged segment to gFreeList. - freeTailp->FreeNext = gFreeList; - gFreeList = freeHeadp; - - gMonitorFreeCount += local_deflated_count; - OM_PERFDATA_OP(Deflations, inc(local_deflated_count)); - } - - if (savedMidInUsep != NULL) { - // deflate_monitor_list_using_JT() detected a safepoint starting. - Thread::muxRelease(&gListLock); - timer.stop(); - { - log_debug(monitorinflation)("pausing deflation of global idle monitors for a safepoint."); - assert(SafepointSynchronize::is_synchronizing(), "sanity check"); - ThreadBlockInVM blocker(cur_jt); - } - // Prepare for another loop after the safepoint. - freeHeadp = NULL; - freeTailp = NULL; - if (log_is_enabled(Info, monitorinflation)) { - timer.start(); - } - Thread::muxAcquire(&gListLock, "deflate_global_idle_monitors_using_JT(2)"); - } - } while (savedMidInUsep != NULL); - Thread::muxRelease(&gListLock); - timer.stop(); - - LogStreamHandle(Debug, monitorinflation) lsh_debug; - LogStreamHandle(Info, monitorinflation) lsh_info; - LogStream * ls = NULL; - if (log_is_enabled(Debug, monitorinflation)) { - ls = &lsh_debug; - } else if (deflated_count != 0 && log_is_enabled(Info, monitorinflation)) { - ls = &lsh_info; - } - if (ls != NULL) { - ls->print_cr("async-deflating global idle monitors, %3.7f secs, %d monitors", timer.seconds(), deflated_count); - } + deflate_common_idle_monitors_using_JT(true /* is_global */, self); } // Deflate per-thread idle ObjectMonitors using a JavaThread. @@ -2147,10 +2118,16 @@ void ObjectSynchronizer::deflate_per_thread_idle_monitors_using_JT() { assert(AsyncDeflateIdleMonitors, "sanity check"); assert(Thread::current()->is_Java_thread(), "precondition"); - JavaThread * cur_jt = JavaThread::current(); + JavaThread * self = JavaThread::current(); + + self->omShouldDeflateIdleMonitors = false; - cur_jt->omShouldDeflateIdleMonitors = false; + deflate_common_idle_monitors_using_JT(false /* !is_global */, self); +} +// Deflate global or per-thread idle ObjectMonitors using a JavaThread. +// +void ObjectSynchronizer::deflate_common_idle_monitors_using_JT(bool is_global, JavaThread * self) { int deflated_count = 0; ObjectMonitor * freeHeadp = NULL; // Local SLL of scavenged ObjectMonitors ObjectMonitor * freeTailp = NULL; @@ -2161,45 +2138,73 @@ timer.start(); } - OM_PERFDATA_OP(MonExtant, inc(cur_jt->omInUseCount)); + if (is_global) { + Thread::muxAcquire(&gListLock, "deflate_global_idle_monitors_using_JT(1)"); + OM_PERFDATA_OP(MonExtant, set_value(gOmInUseCount)); + } else { + OM_PERFDATA_OP(MonExtant, inc(self->omInUseCount)); + } + do { - int local_deflated_count = deflate_monitor_list_using_JT(cur_jt->omInUseList_addr(), &freeHeadp, &freeTailp, &savedMidInUsep); - cur_jt->omInUseCount -= local_deflated_count; + int local_deflated_count; + if (is_global) { + local_deflated_count = deflate_monitor_list_using_JT((ObjectMonitor **)&gOmInUseList, &freeHeadp, &freeTailp, &savedMidInUsep); + gOmInUseCount -= local_deflated_count; + } else { + local_deflated_count = deflate_monitor_list_using_JT(self->omInUseList_addr(), &freeHeadp, &freeTailp, &savedMidInUsep); + self->omInUseCount -= local_deflated_count; + } deflated_count += local_deflated_count; if (freeHeadp != NULL) { // Move the scavenged ObjectMonitors to the global free list. - Thread::muxAcquire(&gListLock, "deflate_per_thread_idle_monitors_using_JT"); guarantee(freeTailp != NULL && local_deflated_count > 0, "freeTailp=" INTPTR_FORMAT ", local_deflated_count=%d", p2i(freeTailp), local_deflated_count); assert(freeTailp->FreeNext == NULL, "invariant"); + if (!is_global) { + Thread::muxAcquire(&gListLock, "deflate_per_thread_idle_monitors_using_JT(2)"); + } // Constant-time list splice - prepend scavenged segment to gFreeList. freeTailp->FreeNext = gFreeList; gFreeList = freeHeadp; gMonitorFreeCount += local_deflated_count; OM_PERFDATA_OP(Deflations, inc(local_deflated_count)); - Thread::muxRelease(&gListLock); - // Prepare for another loop on the current JavaThread. - freeHeadp = NULL; - freeTailp = NULL; + if (!is_global) { + Thread::muxRelease(&gListLock); + } } - timer.stop(); if (savedMidInUsep != NULL) { // deflate_monitor_list_using_JT() detected a safepoint starting. + if (is_global) { + Thread::muxRelease(&gListLock); + } + timer.stop(); { - log_debug(monitorinflation)("jt=" INTPTR_FORMAT ": pausing deflation of per-thread idle monitors for a safepoint.", p2i(cur_jt)); + if (is_global) { + log_debug(monitorinflation)("pausing deflation of global idle monitors for a safepoint."); + } else { + log_debug(monitorinflation)("jt=" INTPTR_FORMAT ": pausing deflation of per-thread idle monitors for a safepoint.", p2i(self)); + } assert(SafepointSynchronize::is_synchronizing(), "sanity check"); - ThreadBlockInVM blocker(cur_jt); + ThreadBlockInVM blocker(self); } - // Prepare for another loop on the current JavaThread after - // the safepoint. + // Prepare for another loop after the safepoint. + freeHeadp = NULL; + freeTailp = NULL; if (log_is_enabled(Info, monitorinflation)) { timer.start(); } + if (is_global) { + Thread::muxAcquire(&gListLock, "deflate_global_idle_monitors_using_JT(3)"); + } } } while (savedMidInUsep != NULL); + if (is_global) { + Thread::muxRelease(&gListLock); + } + timer.stop(); LogStreamHandle(Debug, monitorinflation) lsh_debug; LogStreamHandle(Info, monitorinflation) lsh_info; @@ -2210,7 +2215,11 @@ ls = &lsh_info; } if (ls != NULL) { - ls->print_cr("jt=" INTPTR_FORMAT ": async-deflating per-thread idle monitors, %3.7f secs, %d monitors", p2i(cur_jt), timer.seconds(), deflated_count); + if (is_global) { + ls->print_cr("async-deflating global idle monitors, %3.7f secs, %d monitors", timer.seconds(), deflated_count); + } else { + ls->print_cr("jt=" INTPTR_FORMAT ": async-deflating per-thread idle monitors, %3.7f secs, %d monitors", p2i(self), timer.seconds(), deflated_count); + } } } @@ -2258,9 +2267,13 @@ assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); if (AsyncDeflateIdleMonitors) { - // Nothing to do when idle ObjectMonitors are deflated using a - // JavaThread unless a special cleanup has been requested. if (!is_cleanup_requested()) { + // Mark the JavaThread for idle monitor cleanup if a special + // cleanup has NOT been requested. + if (thread->omInUseCount > 0) { + // This JavaThread is using monitors so mark it. + thread->omShouldDeflateIdleMonitors = true; + } return; } } @@ -2491,12 +2504,13 @@ ": free per-thread monitor must have NULL _header " "field: _header=" INTPTR_FORMAT, p2i(jt), p2i(n), p2i(n->header())); - } else { + *error_cnt_p = *error_cnt_p + 1; + } else if (!AsyncDeflateIdleMonitors) { out->print_cr("ERROR: monitor=" INTPTR_FORMAT ": free global monitor " "must have NULL _header field: _header=" INTPTR_FORMAT, p2i(n), p2i(n->header())); + *error_cnt_p = *error_cnt_p + 1; } - *error_cnt_p = *error_cnt_p + 1; } if (n->object() != NULL) { if (jt != NULL) { --- old/src/hotspot/share/runtime/synchronizer.hpp 2019-04-18 22:07:28.412306922 -0400 +++ new/src/hotspot/share/runtime/synchronizer.hpp 2019-04-18 22:07:27.688306935 -0400 @@ -141,6 +141,7 @@ static void deflate_idle_monitors(DeflateMonitorCounters* counters); static void deflate_global_idle_monitors_using_JT(); static void deflate_per_thread_idle_monitors_using_JT(); + static void deflate_common_idle_monitors_using_JT(bool is_global, JavaThread * self); static void deflate_thread_local_monitors(Thread* thread, DeflateMonitorCounters* counters); static void prepare_deflate_idle_monitors(DeflateMonitorCounters* counters); static void finish_deflate_idle_monitors(DeflateMonitorCounters* counters);