< prev index next >

src/hotspot/share/runtime/objectMonitor.cpp

Print this page
rev 59077 : 8153224.v2.09b.patch combined with 8153224.v2.10.patch; merge with jdk-15+21.
rev 59078 : eosterlund v2.10 CR: reorganize deflate_monitor_using_JT() to use "early exit" style; dcubed - clarify/fix/rearrange a few comments in deflate_monitor_using_JT(); eosterlund v2.10 CR: simplify install_displaced_markword_in_object() and save_om_ptr(); save_om_ptr()'s call to install_displaced_markword_in_object() can race with the deflater thread's clearing of the object field so handle that; fold 8153224.OMHandle_experiment into 8153224.v2.11.patch; merge with jdk-15+21.

*** 238,269 **** } // ----------------------------------------------------------------------------- // Enter support ! void ObjectMonitor::enter(TRAPS) { // The following code is ordered to check the most common cases first // and to reduce RTS->RTO cache line upgrades on SPARC and IA32 processors. Thread * const Self = THREAD; void* cur = try_set_owner_from(NULL, Self); if (cur == NULL) { assert(_recursions == 0, "invariant"); ! return; } if (cur == Self) { // TODO-FIXME: check for integer overflow! BUGID 6557169. _recursions++; ! return; } if (Self->is_lock_owned((address)cur)) { assert(_recursions == 0, "internal state error"); _recursions = 1; set_owner_from_BasicLock(cur, Self); // Convert from BasicLock* to Thread*. ! return; } // We've encountered genuine contention. assert(Self->_Stalled == 0, "invariant"); Self->_Stalled = intptr_t(this); --- 238,269 ---- } // ----------------------------------------------------------------------------- // Enter support ! bool ObjectMonitor::enter(TRAPS) { // The following code is ordered to check the most common cases first // and to reduce RTS->RTO cache line upgrades on SPARC and IA32 processors. Thread * const Self = THREAD; void* cur = try_set_owner_from(NULL, Self); if (cur == NULL) { assert(_recursions == 0, "invariant"); ! return true; } if (cur == Self) { // TODO-FIXME: check for integer overflow! BUGID 6557169. _recursions++; ! return true; } if (Self->is_lock_owned((address)cur)) { assert(_recursions == 0, "internal state error"); _recursions = 1; set_owner_from_BasicLock(cur, Self); // Convert from BasicLock* to Thread*. ! return true; } // We've encountered genuine contention. assert(Self->_Stalled == 0, "invariant"); Self->_Stalled = intptr_t(this);
*** 279,303 **** assert(((oop)object())->mark() == markWord::encode(this), "object mark must match encoded this: mark=" INTPTR_FORMAT ", encoded this=" INTPTR_FORMAT, ((oop)object())->mark().value(), markWord::encode(this).value()); Self->_Stalled = 0; ! return; } assert(_owner != Self, "invariant"); assert(_succ != Self, "invariant"); assert(Self->is_Java_thread(), "invariant"); JavaThread * jt = (JavaThread *) Self; assert(!SafepointSynchronize::is_at_safepoint(), "invariant"); assert(jt->thread_state() != _thread_blocked, "invariant"); ! assert(this->object() != NULL, "invariant"); ! assert(_contentions >= 0, "invariant"); ! // Prevent deflation at STW-time. See deflate_idle_monitors() and is_busy(). ! // Ensure the object-monitor relationship remains stable while there's contention. Atomic::inc(&_contentions); JFR_ONLY(JfrConditionalFlushWithStacktrace<EventJavaMonitorEnter> flush(jt);) EventJavaMonitorEnter event; if (event.should_commit()) { event.set_monitorClass(((oop)this->object())->klass()); --- 279,316 ---- assert(((oop)object())->mark() == markWord::encode(this), "object mark must match encoded this: mark=" INTPTR_FORMAT ", encoded this=" INTPTR_FORMAT, ((oop)object())->mark().value(), markWord::encode(this).value()); Self->_Stalled = 0; ! return true; } assert(_owner != Self, "invariant"); assert(_succ != Self, "invariant"); assert(Self->is_Java_thread(), "invariant"); JavaThread * jt = (JavaThread *) Self; assert(!SafepointSynchronize::is_at_safepoint(), "invariant"); assert(jt->thread_state() != _thread_blocked, "invariant"); ! assert(AsyncDeflateIdleMonitors || this->object() != NULL, "invariant"); ! assert(AsyncDeflateIdleMonitors || contentions() >= 0, "must not be negative: contentions=%d", contentions()); ! // Keep track of contention for JVM/TI and M&M queries. Atomic::inc(&_contentions); + if (AsyncDeflateIdleMonitors && is_being_async_deflated()) { + // Async deflation is in progress and our contentions increment + // above lost the race to async deflation. Undo the work and + // force the caller to retry. + const oop l_object = (oop)object(); + if (l_object != NULL) { + // 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. + install_displaced_markword_in_object(l_object); + } + Self->_Stalled = 0; + Atomic::dec(&_contentions); + return false; + } JFR_ONLY(JfrConditionalFlushWithStacktrace<EventJavaMonitorEnter> flush(jt);) EventJavaMonitorEnter event; if (event.should_commit()) { event.set_monitorClass(((oop)this->object())->klass());
*** 355,365 **** // states will still report that the thread is blocked trying to // acquire it. } Atomic::dec(&_contentions); ! assert(_contentions >= 0, "invariant"); Self->_Stalled = 0; // Must either set _recursions = 0 or ASSERT _recursions == 0. assert(_recursions == 0, "invariant"); assert(_owner == Self, "invariant"); --- 368,378 ---- // states will still report that the thread is blocked trying to // acquire it. } Atomic::dec(&_contentions); ! assert(contentions() >= 0, "must not be negative: contentions=%d", contentions()); Self->_Stalled = 0; // Must either set _recursions = 0 or ASSERT _recursions == 0. assert(_recursions == 0, "invariant"); assert(_owner == Self, "invariant");
*** 391,400 **** --- 404,414 ---- if (event.should_commit()) { event.set_previousOwner((uintptr_t)_previous_owner_tid); event.commit(); } OM_PERFDATA_OP(ContendedLockAttempts, inc()); + return true; } // Caveat: TryLock() is not necessarily serializing if it returns failure. // Callers must compensate as needed.
*** 410,425 **** // We can either return -1 or retry. // Retry doesn't make as much sense because the lock was just acquired. return -1; } // Convert the fields used by is_busy() to a string that can be // used for diagnostic output. const char* ObjectMonitor::is_busy_to_string(stringStream* ss) { ! ss->print("is_busy: contentions=%d, waiters=%d, owner=" INTPTR_FORMAT ! ", cxq=" INTPTR_FORMAT ", EntryList=" INTPTR_FORMAT, _contentions, ! _waiters, p2i(_owner), p2i(_cxq), p2i(_EntryList)); return ss->base(); } #define MAX_RECHECK_INTERVAL 1000 --- 424,503 ---- // We can either return -1 or retry. // Retry doesn't make as much sense because the lock was just acquired. return -1; } + // 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"); + + const oop l_object = (oop)object(); + if (l_object == NULL) { + // ObjectMonitor's object ref has already been cleared by async + // deflation so we're done here. + return; + } + ADIM_guarantee(l_object == obj, "object=" INTPTR_FORMAT " must equal obj=" + INTPTR_FORMAT, p2i(l_object), p2i(obj)); + + markWord dmw = header(); + // The dmw has to be neutral (not NULL, not locked and not marked). + ADIM_guarantee(dmw.is_neutral(), "must be neutral: dmw=" INTPTR_FORMAT, dmw.value()); + + // 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. + markWord res = obj->cas_set_mark(dmw, markWord::encode(this)); + if (res != markWord::encode(this)) { + // This should be rare so log at the Info level when it happens. + log_info(monitorinflation)("install_displaced_markword_in_object: " + "failed cas_set_mark: new_mark=" INTPTR_FORMAT + ", old_mark=" INTPTR_FORMAT ", res=" INTPTR_FORMAT, + dmw.value(), markWord::encode(this).value(), + res.value()); + } + + // 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. + } + // Convert the fields used by is_busy() to a string that can be // used for diagnostic output. const char* ObjectMonitor::is_busy_to_string(stringStream* ss) { ! ss->print("is_busy: waiters=%d, ", _waiters); ! if (!AsyncDeflateIdleMonitors) { ! ss->print("contentions=%d, ", contentions()); ! ss->print("owner=" INTPTR_FORMAT, p2i(_owner)); ! } else { ! if (contentions() > 0) { ! ss->print("contentions=%d, ", contentions()); ! } else { ! ss->print("contentions=0"); ! } ! if (_owner != DEFLATER_MARKER) { ! ss->print("owner=" INTPTR_FORMAT, p2i(_owner)); ! } else { ! // We report NULL instead of DEFLATER_MARKER here because is_busy() ! // ignores DEFLATER_MARKER values. ! ss->print("owner=" INTPTR_FORMAT, NULL); ! } ! } ! ss->print(", cxq=" INTPTR_FORMAT ", EntryList=" INTPTR_FORMAT, p2i(_cxq), ! p2i(_EntryList)); return ss->base(); } #define MAX_RECHECK_INTERVAL 1000
*** 434,443 **** --- 512,531 ---- assert(_owner == Self, "invariant"); assert(_Responsible != Self, "invariant"); return; } + if (AsyncDeflateIdleMonitors && + try_set_owner_from(DEFLATER_MARKER, Self) == DEFLATER_MARKER) { + // The deflation protocol finished the first part (setting owner), + // but it failed the second part (making contentions negative) and + // bailed. Acquired the monitor. + assert(_succ != Self, "invariant"); + assert(_Responsible != Self, "invariant"); + return; + } + assert(InitDone, "Unexpectedly not initialized"); // We try one round of spinning *before* enqueueing Self. // // If the _owner is ready but OFFPROC we could use a YieldTo()
*** 550,559 **** --- 638,655 ---- Self->_ParkEvent->park(); } if (TryLock(Self) > 0) break; + if (AsyncDeflateIdleMonitors && + try_set_owner_from(DEFLATER_MARKER, Self) == DEFLATER_MARKER) { + // The deflation protocol finished the first part (setting owner), + // but it failed the second part (making contentions negative) and + // bailed. Acquired the monitor. + break; + } + // The lock is still contested. // Keep a tally of the # of futile wakeups. // Note that the counter is not protected by a lock or updated by atomics. // That is by design - we trade "lossy" counters which are exposed to // races during updates for a lower probe effect.
*** 814,824 **** // inopportune) reclamation of "this". // // We'd like to assert that: (THREAD->thread_state() != _thread_blocked) ; // There's one exception to the claim above, however. EnterI() can call // exit() to drop a lock if the acquirer has been externally suspended. ! // In that case exit() is called with _thread_state as _thread_blocked, // but the monitor's _contentions field is > 0, which inhibits reclamation. // // 1-0 exit // ~~~~~~~~ // ::exit() uses a canonical 1-1 idiom with a MEMBAR although some of --- 910,920 ---- // inopportune) reclamation of "this". // // We'd like to assert that: (THREAD->thread_state() != _thread_blocked) ; // There's one exception to the claim above, however. EnterI() can call // exit() to drop a lock if the acquirer has been externally suspended. ! // In that case exit() is called with _thread_state == _thread_blocked, // but the monitor's _contentions field is > 0, which inhibits reclamation. // // 1-0 exit // ~~~~~~~~ // ::exit() uses a canonical 1-1 idiom with a MEMBAR although some of
*** 1089,1099 **** // Hygiene -- once we've set _owner = NULL we can't safely dereference Wakee again. // The thread associated with Wakee may have grabbed the lock and "Wakee" may be // out-of-scope (non-extant). Wakee = NULL; ! // Drop the lock // Uses a fence to separate release_store(owner) from the LD in unpark(). release_clear_owner(Self); OrderAccess::fence(); DTRACE_MONITOR_PROBE(contended__exit, this, object(), Self); --- 1185,1195 ---- // Hygiene -- once we've set _owner = NULL we can't safely dereference Wakee again. // The thread associated with Wakee may have grabbed the lock and "Wakee" may be // out-of-scope (non-extant). Wakee = NULL; ! // Drop the lock. // Uses a fence to separate release_store(owner) from the LD in unpark(). release_clear_owner(Self); OrderAccess::fence(); DTRACE_MONITOR_PROBE(contended__exit, this, object(), Self);
*** 1137,1156 **** return save; } // reenter() enters a lock and sets recursion count // complete_exit/reenter operate as a wait without waiting ! void ObjectMonitor::reenter(intx recursions, TRAPS) { Thread * const Self = THREAD; assert(Self->is_Java_thread(), "Must be Java thread!"); JavaThread *jt = (JavaThread *)THREAD; guarantee(_owner != Self, "reenter already owner"); ! enter(THREAD); // enter the monitor guarantee(_recursions == 0, "reenter recursion"); _recursions = recursions; ! return; } // Checks that the current THREAD owns this monitor and causes an // immediate return if it doesn't. We don't use the CHECK macro // because we want the IMSE to be the only exception that is thrown --- 1233,1255 ---- return save; } // reenter() enters a lock and sets recursion count // complete_exit/reenter operate as a wait without waiting ! bool ObjectMonitor::reenter(intx recursions, TRAPS) { Thread * const Self = THREAD; assert(Self->is_Java_thread(), "Must be Java thread!"); JavaThread *jt = (JavaThread *)THREAD; guarantee(_owner != Self, "reenter already owner"); ! if (!enter(THREAD)) { ! return false; ! } ! // Entered the monitor. guarantee(_recursions == 0, "reenter recursion"); _recursions = recursions; ! return true; } // Checks that the current THREAD owns this monitor and causes an // immediate return if it doesn't. We don't use the CHECK macro // because we want the IMSE to be the only exception that is thrown
*** 1960,1977 **** // Print the ObjectMonitor like a debugger would: // // (ObjectMonitor) 0x00007fdfb6012e40 = { // _header = 0x0000000000000001 // _object = 0x000000070ff45fd0 ! // _next_om = 0x0000000000000000 // _pad_buf0 = { // [0] = '\0' // ... ! // [103] = '\0' // } // _owner = 0x0000000000000000 // _previous_owner_tid = 0 // _recursions = 0 // _EntryList = 0x0000000000000000 // _cxq = 0x0000000000000000 // _succ = 0x0000000000000000 // _Responsible = 0x0000000000000000 --- 2059,2082 ---- // Print the ObjectMonitor like a debugger would: // // (ObjectMonitor) 0x00007fdfb6012e40 = { // _header = 0x0000000000000001 // _object = 0x000000070ff45fd0 ! // _allocation_state = Old // _pad_buf0 = { // [0] = '\0' // ... ! // [43] = '\0' // } // _owner = 0x0000000000000000 // _previous_owner_tid = 0 + // _pad_buf1 = { + // [0] = '\0' + // ... + // [47] = '\0' + // } + // _next_om = 0x0000000000000000 // _recursions = 0 // _EntryList = 0x0000000000000000 // _cxq = 0x0000000000000000 // _succ = 0x0000000000000000 // _Responsible = 0x0000000000000000
*** 1985,2010 **** // void ObjectMonitor::print_debug_style_on(outputStream* st) const { st->print_cr("(ObjectMonitor*) " INTPTR_FORMAT " = {", p2i(this)); st->print_cr(" _header = " INTPTR_FORMAT, header().value()); st->print_cr(" _object = " INTPTR_FORMAT, p2i(_object)); ! st->print_cr(" _next_om = " INTPTR_FORMAT, p2i(next_om())); st->print_cr(" _pad_buf0 = {"); st->print_cr(" [0] = '\\0'"); st->print_cr(" ..."); st->print_cr(" [%d] = '\\0'", (int)sizeof(_pad_buf0) - 1); st->print_cr(" }"); st->print_cr(" _owner = " INTPTR_FORMAT, p2i(_owner)); st->print_cr(" _previous_owner_tid = " JLONG_FORMAT, _previous_owner_tid); st->print_cr(" _recursions = " INTX_FORMAT, _recursions); st->print_cr(" _EntryList = " INTPTR_FORMAT, p2i(_EntryList)); st->print_cr(" _cxq = " INTPTR_FORMAT, p2i(_cxq)); st->print_cr(" _succ = " INTPTR_FORMAT, p2i(_succ)); st->print_cr(" _Responsible = " INTPTR_FORMAT, p2i(_Responsible)); st->print_cr(" _Spinner = %d", _Spinner); st->print_cr(" _SpinDuration = %d", _SpinDuration); ! st->print_cr(" _contentions = %d", _contentions); st->print_cr(" _WaitSet = " INTPTR_FORMAT, p2i(_WaitSet)); st->print_cr(" _waiters = %d", _waiters); st->print_cr(" _WaitSetLock = %d", _WaitSetLock); st->print_cr("}"); } --- 2090,2131 ---- // void ObjectMonitor::print_debug_style_on(outputStream* st) const { st->print_cr("(ObjectMonitor*) " INTPTR_FORMAT " = {", p2i(this)); st->print_cr(" _header = " INTPTR_FORMAT, header().value()); st->print_cr(" _object = " INTPTR_FORMAT, p2i(_object)); ! st->print(" _allocation_state = "); ! if (is_free()) { ! st->print("Free"); ! } else if (is_old()) { ! st->print("Old"); ! } else if (is_new()) { ! st->print("New"); ! } else { ! st->print("unknown=%d", _allocation_state); ! } ! st->cr(); st->print_cr(" _pad_buf0 = {"); st->print_cr(" [0] = '\\0'"); st->print_cr(" ..."); st->print_cr(" [%d] = '\\0'", (int)sizeof(_pad_buf0) - 1); st->print_cr(" }"); st->print_cr(" _owner = " INTPTR_FORMAT, p2i(_owner)); st->print_cr(" _previous_owner_tid = " JLONG_FORMAT, _previous_owner_tid); + st->print_cr(" _pad_buf1 = {"); + st->print_cr(" [0] = '\\0'"); + st->print_cr(" ..."); + st->print_cr(" [%d] = '\\0'", (int)sizeof(_pad_buf1) - 1); + st->print_cr(" }"); + st->print_cr(" _next_om = " INTPTR_FORMAT, p2i(next_om())); st->print_cr(" _recursions = " INTX_FORMAT, _recursions); st->print_cr(" _EntryList = " INTPTR_FORMAT, p2i(_EntryList)); st->print_cr(" _cxq = " INTPTR_FORMAT, p2i(_cxq)); st->print_cr(" _succ = " INTPTR_FORMAT, p2i(_succ)); st->print_cr(" _Responsible = " INTPTR_FORMAT, p2i(_Responsible)); st->print_cr(" _Spinner = %d", _Spinner); st->print_cr(" _SpinDuration = %d", _SpinDuration); ! st->print_cr(" _contentions = %d", contentions()); st->print_cr(" _WaitSet = " INTPTR_FORMAT, p2i(_WaitSet)); st->print_cr(" _waiters = %d", _waiters); st->print_cr(" _WaitSetLock = %d", _WaitSetLock); st->print_cr("}"); }
< prev index next >