- Loading...
...
RFE: 8153224 Monitor deflation prolong safepoints
https://bugs.openjdk.java.net/browse/JDK-8153224
Full Webrev: http://cr.openjdk.java.net/~dcubed/8153224-webrev/12-for-jdk1417-for-jdk15+24.v2.0915.full/
Inc Webrev: http://cr.openjdk.java.net/~dcubed/8153224-webrev/1217-for-jdk14jdk15+24.v2.0915.inc/
This patch for Async Monitor Deflation is based on Carsten Varming's
...
The current idle monitor deflation mechanism executes at a safepoint during cleanup operations. Due to this execution environment, the current mechanism does not have to worry about interference from concurrently executing JavaThreads. Async Monitor Deflation uses the ServiceThread to deflate idle monitors so the new mechanism has to detect interference and adapt as appropriate. In other words, data races are natural part of Async Monitor Deflation and the algorithms have to detect the races and react without data loss or corruption.
ObjectSynchronizer::deflate_monitor_using_JT() is the new counterpart to ObjectSynchronizer::deflate_monitor() and does the heavy lifting of asynchronously deflating a monitor using a three part prototcol:
Async Monitor Deflation is performed in two stages: stage one performs the two part protocol described in "Deflation With Interference Detection" below and moves the async deflated ObjectMonitors from an in-use list to a global wait list; the ServiceThread performs a handshake (or a safepoint) with all other JavaThreads after stage one is complete and that forces any racing threads to make forward progress; stage two moves the ObjectMonitors from the global wait list to the global free list. The special values that mark an ObjectMonitor as async deflated remain in their fields until the ObjectMonitor is moved from the global free list to a per-thread free list which is sometime after stage two has completed.
ObjectSynchronizer::deflate_monitor_using_JT() is the new counterpart to ObjectSynchronizer::deflate_monitor() and does the heavy lifting of asynchronously deflating a monitor using a two part prototcol:
If we lose any of the races, the monitor cannot be deflated at this time.
Once we know it is safe to deflate the monitor (which is mostly field resetting and monitor list management), we have to restore the object's header. That's another racy operation that is described below in "Restoring the Header With Interference Detection".
The setting of the special values that mark an ObjectMonitor as async deflated and the restoration of the object's header comprise the first stage of Async Monitor Deflation.
ObjectMonitor::install_displaced_markword_in_object() is the new piece of code that handles all the racy situations with restoring an object's header asynchronously. The function is called from two three places (deflation and saving an ObjectMonitor* in an ObjectMonitorHandle). The restoration protocol for the object's header uses the mark bit along with the hash() value staying at zero to indicate that the object's header is being restored, ObjectMonitor::enter(), and FastHashCode). Only one of the possible racing scenarios can win and the losing scenarios all adapt to the winning scenario's object header value.
...
Various code paths have been updated to recognize an owner field equal to DEFLATER_MARKER or a negative ref_count contentions field and those code paths will retry their operation. This is the shortest "Key Part" description, but don't be fooled. See "Gory Details" below.
ObjectMonitor::save_om_ptrenter() is used to safely save an ObjectMonitor* in an ObjectMonitorHandlecan change an idle monitor into a busy monitor. ObjectSynchronizer::deflate_monitor_using_JT() is used to asynchronously deflate an idle monitor. save_om_ptrenter() and deflate_monitor_using_JT() can interfere with each other. The thread calling save_om_ptrenter() (T-saveenter) is potentially racing with another JavaThread (T-deflate) so both threads have to check the results of the races.
T-save enter ObjectMonitor T-deflate
------------------------ +-----------------------+ ----------------------------------------
save_om_ptrenter() { | owner=NULL | deflate_monitor_using_JT() {
1> atomic inc ref_countcontentions | ref_countcontentions=0 | 1> cmpxchg(DEFLATER_MARKER, &owner, NULL)
+-----------------------+
T-save enter ObjectMonitor T-deflate
------------------------ +-----------------------+ --------------------------------------------
save_om_ptrenter() { | owner=DEFLATER_MARKER | deflate_monitor_using_JT() {
1> atomic inc ref_countadd_to_contentions(1) | ref_countcontentions=0 | cmpxchg(try_set_owner_from(NULL, DEFLATER_MARKER, &owner, NULL)
+-----------------------+ :
1> prev = cmpxchg(&contentions, 0, -max_jint, &ref_count, 0)
T-save enter ObjectMonitor ObjectMonitor T-deflate
---------------------------------- +-------------------------+ --------------------------------------------
save_om_ptrenter() { | owner=DEFLATER_MARKER | deflate_monitor_using_JT() {
atomic inc ref_countadd_to_contentions(1) | ref_countcontentions=-max_jint+1 | cmpxchg(try_set_owner_from(NULL, DEFLATER_MARKER, &owner, NULL)
1> if (owner == DEFLATER_MARKER &&is_being_async_deflated()) { +-------------------------+ :
restore obj header ref_count <= 0) { || prev = cmpxchg(&contentions, 0, -max_jint, &ref_count, 0)
restore obj headeradd_to_contentions(-1) \/ 1> if (prev == 0) &&
atomic dec ref_count {
2> return false to force retry +-------------------------+ owner == DEFLATER_MARKER) {
2> return false to force retry restore obj header
} | owner=DEFLATER_MARKER | 2> finish restorethe obj headerdeflation
} | ref_countcontentions=-max_jint | 2> finish the deflation}
+-------------------------+ }
T-save enter ObjectMonitor T-deflate
---------------------------------- +-------------------------+ ---------------------------------------------
save_om_ptrenter() { | owner=DEFLATER_MARKER. | deflate_monitor_using_JT() {
atomic inc ref_count add_to_contentions(1) | ref_countcontentions=1 | cmpxchg(try_set_owner_from(NULL, DEFLATER_MARKER, &owner, NULL)
1> if (owner == DEFLATER_MARKER &&is_being_async_deflated()) { +-------------------------+ :
} ref_count <= 0) { || prev = cmpxchg(-max_jint, &ref_count, 0)
} else { || prev = cmpxchg(&contentions, 0, -max_jint)
2> <continue contended enter> \/ 1> if (prev == 0) &&
{
save om_ptr in the +-------------------------+ owner == DEFLATER_MARKER)} else {
ObjectMonitorHandle | owner=NULL | } else {
try_set_owner_from(DEFLATER_MARKER, NULL)
2> return true | ref_countcontentions=1 | cmpxchg(NULL, &owner, DEFLATER_MARKER)2> return
+-------------------------+ 2> return
Sorry in advance for the sudden deep dive into really gory C2 details, but this is related to a majority of save_om_ptr() so this is the right place to talk about the complication.
As of CR7/v2.07/10-for-jdk14, we have added C2 inc_om_ref_count() on X64 to implement the ref_count management parts of save_om_ptr():
T-enter ObjectMonitor T-deflate
-------------------------------------------- +-------------------------+ ------------------------------------------
ObjectMonitor::enter() { | owner=DEFLATER_MARKER | deflate_monitor_using_JT() {
<owner is contended> | ref_count=1 | cmpxchg(DEFLATER_MARKER, &owner, NULL)
1> EnterI() { +-------------------------+ 1> :
if (owner == DEFLATER_MARKER && || 2> : <thread_stalls>
cmpxchg(Self, &owner, \/ :
DEFLATER_MARKER) +-------------------------+ :
== DEFLATER_MARKER) { | owner=Self/T-enter | :
// EnterI is done | ref_count=0 | : <thread_resumes>
return +-------------------------+ prev = cmpxchg(-max_jint, &ref_count, 0)
} || if (prev == 0 &&
} // enter() is done \/ 3> owner == DEFLATER_MARKER) {
~OMH: atomic dec ref_count +-------------------------+ } else {
2> : <does app work> | owner=Self/T-enter|NULL | cmpxchg(NULL, &owner, DEFLATER_MARKER)
3> : | ref_count=-max_jint | atomic add max_jint to ref_count
exit() monitor +-------------------------+ 4> bailout on deflation
4> owner = NULL || }
\/
+-------------------------+
| owner=Self/T-enter|NULL |
| ref_count=0 |
+-------------------------+
NULL → DEFLATE_MARKER → Self/T-enter
so we really have A1-B-A2, but the A-B-A principal still holds.
If the T-enter thread has managed to enter and exit the monitor during the T-deflate stall, then our owner field A-B-A transition is:
NULL → DEFLATE_MARKER → Self/T-enter → NULL
so we really have A-B1-B2-A, but the A-B-A principal still holds.
T-enter finished doing app work and is about to exit the monitor (or it has already exited the monitor).
The fourth ObjectMonitor box is showing the fields at this point and the "4>" markers are showing where each thread is at for that ObjectMonitor box.
After T-deflate has won the race for deflating an ObjectMonitor it has to restore the header in the associated object. Of course another thread can be trying to do something to the object's header at the same time. Isn't asynchronous work exciting?!?!
ObjectMonitor::install_displaced_markword_in_object() is called from two places so we can have a race between a T-save thread and a T-deflate thread:
T-save object T-deflate
------------------------------------------- +-------------+ --------------------------------------------
install_displaced_markword_in_object() { | mark=om_ptr | install_displaced_markword_in_object() {
dmw = header() +-------------+ dmw = header()
if (!dmw->is_marked() && if (!dmw->is_marked() &&
dmw->hash() == 0) { dmw->hash() == 0) {
create marked_dmw create marked_dmw
dmw = cmpxchg(marked_dmw, &header, dmw) dmw = cmpxchg(marked_dmw, &header, dmw)
} }
T-save object T-deflate
------------------------------------------- +-------------+ -------------------------------------------
install_displaced_markword_in_object() { | mark=om_ptr | install_displaced_markword_in_object() {
dmw = header() +-------------+ dmw = header()
if (!dmw->is_marked() && if (!dmw->is_marked() &&
dmw->hash() == 0) { dmw->hash() == 0) {
create marked_dmw create marked_dmw
dmw = cmpxchg(marked_dmw, &header, dmw) dmw = cmpxchg(marked_dmw, &header, dmw)
} }
// dmw == marked_dmw here // dmw == original dmw here
if (dmw->is_marked()) if (dmw->is_marked())
unmark dmw unmark dmw
obj = object() obj = object()
obj->cas_set_mark(dmw, this) obj->cas_set_mark(dmw, this)
T-save object T-deflate
------------------------------------------- +-------------+ -------------------------------------------
install_displaced_markword_in_object() { | mark=om_ptr | install_displaced_markword_in_object() {
dmw = header() +-------------+ dmw = header()
if (!dmw->is_marked() && if (!dmw->is_marked() &&
dmw->hash() == 0) { dmw->hash() == 0) {
create marked_dmw create marked_dmw
dmw = cmpxchg(marked_dmw, &header, dmw) dmw = cmpxchg(marked_dmw, &header, dmw)
} }
// dmw == original dmw here // dmw == marked_dmw here
if (dmw->is_marked()) if (dmw->is_marked())
unmark dmw unmark dmw
obj = object() obj = object()
obj->cas_set_mark(dmw, this) obj->cas_set_mark(dmw, this)
T-enter T-save object T-deflate
------------------------------------------- +-------------+ -------------------------------------------
install_displaced_markword_in_object() { | mark=dmw | install_displaced_markword_in_object() {
dmw = header() +-------------+ dmw = header()
if (!dmw->is_marked() && ObjectMonitor if (!dmw->is_marked() &&
dmw->hash() == 0) { dmw->hash() == 0) {
create marked_dmw create marked_dmw
dmw = cmpxchg(marked_dmw, &header, dmw) dmw = cmpxchg(marked_dmw, &header, dmw)
} }
// dmw == ... T-deflate
-------------------------------------------- +-------------------------+ --------------------------------------------
ObjectMonitor::enter() { // dmw| == ...
owner=DEFLATER_MARKER if (dmw->is_marked()) if (dmw->is_marked())
unmark dmw unmark dmw
obj = object() obj = object()
obj->cas_set_mark(dmw, this) obj->cas_set_mark(dmw, this)
Please notice that install_displaced_markword_in_object() does not do any retries on any code path:
If we have a race between a T-deflate thread and a thread trying to get/set a hashcode (T-hash), then the race is between the ObjectMonitorHandle.save_om_ptr(obj, mark) call in T-hash and deflation protocol in T-deflate.
T-hash ObjectMonitor T-deflate
| deflate_monitor_using_JT() {
add_to_contentions(1) | contentions=1 | try_set_owner_from(NULL, DEFLATER_MARKER)
1> EnterI() { +-------------------------+ 1> :
if (try_set_owner_from(DEFLATER_MARKER, || 2> : <thread_stalls>
Self) == DEFLATER_MARKER) { \/ :
// Add marker for cancellation +------------------------------------- ++ :
add_to_contentions(1) | owner=Self/T-enter | :
// EnterI is done | contentions=2 | : <thread_resumes>
return +-------------------------+ ----------------------------------------
save_om_ptr() { | owner=NULL | deflate_monitor_using_JT() {
: | ref_count=0 | 1> cmpxchg(DEFLATER_MARKER, &owner, NULL)
1> atomic inc ref_count +-----------------------+
T-hash ObjectMonitor T-deflate
---------------------- +--- prev = cmpxchg(&contentions, 0, -max_jint)
} || if (prev == 0) {
2> add_to_contentions(-1) \/ 3> } else {
} // enter() is done +--------------------+ ------------------------------------------
save_om_ptr() { | owner=DEFLATER_MARKER | deflate_monitor_using_JT() {
: + if (try_set_owner_from(DEFLATER_MARKER,
: <does app work> | ref_count=0 | cmpxchg(DEFLATER_MARKER, &owner, NULL)
1> atomic inc ref_count +-----------------------+| owner=Self/T-enter|NULL | if (contentions != 0 || waitersNULL) != 0DEFLATER_MARKER) {
3> : }
| contentions=1 | add_to_contentions(-1)
exit() monitor 1> prev = cmpxchg(-max_jint, &ref_count, 0)
If T-deflate wins the race, then T-hash will have to retry at most once.
T-hash ObjectMonitor T-deflate
+------------------------- +-----------------------+ ------------------------------------------
save_om_ptr() { | owner=DEFLATER_MARKER | deflate_monitor_using_JT() {
1> atomic inc ref_count | ref_count=-max_jint | cmpxchg(DEFLATER_MARKER, &owner, NULL)
if (owner == +-----------------------+ if (contentions != 0 || waiters != 0) {
DEFLATER_MARKER && + }
4> owner = NULL || 4> bailout on deflation
\/ || }
ref_count <= 0) { \/ prev = cmpxchg(-max_jint, &ref_count, 0)
restore obj header +-------------------------+ 1> if (prev == 0 &&
atomic dec ref_count | owner=DEFLATER_MARKER | Self/T-enter|NULL |
owner == DEFLATER_MARKER) {
2> return false to | ref_count=-max_jint | restore obj header
cause a retry +-----------------------+ 2> finish the deflation
| contentions=0 } |
}+-------------------------+
If T-hash wins the race, then the ref_count will cause T-deflate to bail out on deflating the monitor.
Note: header is not mentioned in any of the previous sections for simplicity.
T-enter finished doing app work and is about to exit the monitor (or it has already exited the monitor).
The fourth ObjectMonitor box is showing the fields at this point and the "4>" markers are showing where each thread is at for that ObjectMonitor box.
After T-deflate has won the race for deflating an ObjectMonitor it has to restore the header in the associated object. Of course another thread can be trying to do something to the object's header at the same time. Isn't asynchronous work exciting?!?!
ObjectMonitor::install_displaced_markword_in_object() is called from two places so we can have a race between a T-enter thread and a T-deflate thread:
T-enter object T-deflate
------------------------------------- T-hash ObjectMonitor T-deflate
------------------------- +-----------------------+ ------------------------------------------
save_om_ptr() { | header=dmw_no_hash | deflate_monitor_using_JT() {
atomic inc ref_count | owner=DEFLATER_MARKER | cmpxchg(DEFLATER_MARKER, &owner, NULL)
1> if (owner == | ref_count=1 | if (contentions != 0 || waiters != 0) {
DEFLATER_MARKER && +-----------------------+ }
ref_count <= 0) { || 1> prev = cmpxchg(-max_jint, &ref_count, 0)
} else { \/ if (prev == 0 &&
2> save om_ptr in the +---------- +-------------+ owner == DEFLATER_MARKER) {
ObjectMonitorHandle | header=dmw_no_hash | } else {
return true | owner=NULL | cmpxchg(NULL, &owner, DEFLATER_MARKER)
} -----------------------------------------------
install_displaced_markword_in_object(oop obj) { | mark=om_ptr | install_displaced_markword_in_object(oop obj) {
dmw = header() +-------------+ dmw = | ref_count=1 | 2> bailout on deflation
} +-----------------------+ }
if save_om_ptr() {header()
obj->cas_set_mark(dmw, this) obj->cas_set_mark(dmw, this) ||
if no hash \/
gen hash & merge +}
T-enter object T-deflate
----------------------------------------------- +-------------+ -----------------------------------------------+
hash = hash(header) | header=dmw_hash |
} | owner=NULL |
3> atomic dec ref_count | ref_count=1 |
return hash install_displaced_markword_in_object(oop obj) { | mark=dmw | install_displaced_markword_in_object(oop obj) {
dmw = header() +-----------------------+
...
dmw = header()
obj->cas_set_mark(dmw, this) obj->cas_set_mark(dmw, this)
Please notice that install_displaced_markword_in_object() does not do any retries on any code path:
There are a few races that can occur between a T-deflate thread and a thread trying to get/set a hashcode (T-hash) in an ObjectMonitor:
The common fall thru code path (executed by T-hash) that inflates the ObjectMonitor in order to set the hashcode can race with an async deflation (T-deflate). After the hashcode has been stored in the ObjectMonitor, we (T-hash) check if the ObjectMonitor has been async deflated (by T-deflate). If it has, then we (T-hash) retry because we don't know if the hashcode was stored in the ObjectMonitor before the object's header was restored (by T-deflate). Retrying (by T-hash) will result in the hashcode being stored in either object's header or in the re-inflated ObjectMonitor's header as appropriate.
Use of specialized measurement code with the CR5/v2.05/8-for-jdk13 bits revealed that the gListLock contention is responsible for much of the performance degradation observed with SPECjbb2015. Consequently the primary focus of the next round of changes is/was on switching from course grained Thread::muxAcquire(&gListLock) and Thread::muxRelease(&gListLock) pairs to spin-lock monitor list management. Of course, since the Java Monitor subsystem is full of special cases, the spin-lock list management code has to have a number of special cases which are described here.
The Spin-Lock Monitor List management code was pushed to JDK15 using the following bug id:
JDK-8235795 replace monitor list mux{Acquire,Release}(&gListLock) with spin locks
The Async Monitor Deflation project makes a few additional changes on top of what was pushed via JDK-8235795.
There is one simple case of spin-lock list management with the Java Monitor subsystem so we'll start with that code as a way to introduce the spin-lock concepts:
L1: while (true) {
L2: PaddedObjectMonitor* cur = Atomic::load(&g_block_list);
L3: Atomic::store(&new_blk[0]._next_om, cur);
L4: if (Atomic::cmpxchg(&g_block_list, cur, new_blk) == cur) {
L5: Atomic::add(&LVarsom_list_globals.population, _BLOCKSIZE - 1);
L6: break;
L7: }
L8: }
...
...
...
to achieve the safe update of the 'g_block_list' value; the atomic increment of the 'LVarsom_list_globals.population' counter is considered to be just accounting (pun intended).
...
Note: This subsection is talking about "Simple Take" and "Simple Prepend" in abstract terms. The purpose of this code and A-B-A example is to introduce the race concepts. The code shown here is not an exact match for the project code and the specific A-B-A example is not (currently) found in the project code.
...
The purpose of this subsection is to provide background information about how ObjectMonitors move between the various lists. This project changes the way these movements are implemented, but does not change the movements themselves. For example, newly allocated blocks of ObjectMonitors are always prepending to the global free list; this is true in the baseline and is true in this project. One exception is the optional addition of the global wait list (see below).
...
ObjectMonitors are deflated at a safepoint by:
ObjectSynchronizer::deflate_monitor_list() calling ObjectSynchronizer::deflate_monitor()
And when Async Monitor Deflation is enabled, they are deflated by:
ObjectSynchronizer::deflate_monitor_list_using_JT() calling ObjectSynchronizer::deflate_monitor_using_JT()
Idle ObjectMonitors are deflated by the ServiceThread when Async Monitor Deflation is enabled. They can also be deflated at a safepoint by the VMThread or by a task worker thread. Safepoint deflation is used when Async Monitor Deflation is disabled or when there is a special deflation request, e.g., System.gc()..gc().
An idle ObjectMonitor is deflated and extracted from its in-use list and prepended to the global wait list. The in-use list can be either the global in-use list or a per-thread in-use list. Deflated ObjectMonitors are always prepended to the global wait list.
An idle ObjectMonitor is deflated and extracted from its in-use list and prepended to the global free list. The in-use list can be either the global in-use list or a per-thread in-use list. Deflated ObjectMonitors are always prepended to the global free list.
...
...
L01: while (true) {
L02: om_lock(m); // Lock m so we can safely update its next field.
L03: ObjectMonitor* cur = NULL;
L04: // Lock the list head to guard against A-B-A race:
L05: if ((cur = get_list_head_locked(list_p)) != NULL) {
L06: // List head is now locked so we can safely switch it.
L07: setm->set_next_om(m, cur); // m now points to cur (and unlocks m)
L08: Atomic::store(list_p, m); // Switch list head to unlocked m.
L09: om_unlock(cur);
L10: break;
L11: }
L12: // The list is empty so try to set the list head.
L13: assert(cur == NULL, "cur must be NULL: cur=" INTPTR_FORMAT, p2i(cur));
L14: setm->set_next_om(m, cur); // m now points to NULL (and unlocks m)
L15: if (Atomic::cmpxchg(list_p, cur, m) == cur) {
L16: // List head is now unlocked m.
L17: break;
L18: }
L19: // Implied else: try it all again
L20: }
L21: Atomic::inc(count_p);
...
ObjectMonitor 'm' is safely on the list at the point that we have updated 'list_p' to refer to 'm'. In this subsection's block of code, we also called three new functions: om_lock(), get_list_head_locked() and set_next_om(), that are explained in the next few subsections about helper functions.
Note: The above code snippet comes from prepend_to_common(); see that function for more context and a few more comments.
Managing spin-locks on ObjectMonitors has been abstracted into a few helper functions. try_om_lock() is the first interesting one:
L1: static bool try_om_lock(ObjectMonitor* om) {
L2: // Get current next field without any OM_LOCK_BIT value.
L3: ObjectMonitor* next = (ObjectMonitor*)((intptr_t)Atomic::load(&om->_next_om) & ~OM_LOCK_BITunmarked_next(om);
L4: if (Atomic::cmpxchg(&om->>try_set_next_om, (next, mark_om_ptr(next)) != next) {
L5: return false; // Cannot lock the ObjectMonitor.
L6: }
L7: return true;
L8: }
...
set_next_om() is the next interesting function and it also only needs a quick explanation:
L1: staticinline void ObjectMonitor::set_next_om(ObjectMonitor* om, ObjectMonitor* value) {
L2: Atomic::store(&om->_next_om, value);
L3: }
...
...
L01: if (from_per_thread_alloc) {
L02: if ((mid = get_list_head_locked(&self->om_in_use_list)) == NULL) {
L03: fatal("thread=" INTPTR_FORMAT " in-use list must not be empty.", p2i(self));
L04: }
L05: next = unmarked_next(mid);
L06: while (true) {
L07: if (m == mid) {
L08: if (cur_mid_in_use == NULL) {
L09: L07: Atomic::store(&self->om_in_use_list, next);
L10L08: } else if (m == next) {
L09: mid = next;
L10: om_lock(mid);
L11: set next = unmarked_next(cur_mid_in_use, next);
L12: } self->om_in_use_list->set_next_om(next);
L13: extracted} =else true;{
L14: Atomic::dec(&self->om_in_use_count) ObjectMonitor* anchor = next;
L15: om_unlocklock(midanchor);
L16: break om_unlock(mid);
L17: } while ((mid = unmarked_next(anchor)) != NULL) {
L18: if (cur_mid_in_use !m == NULLmid) {
L19: om_unlock(cur_mid_in_use next = unmarked_next(mid);
L20: } anchor->set_next_om(next);
L21: cur_mid_in_use = mid break;
L22: mid} =else next;{
L23: if om_lock(mid == NULL) {);
L24: fatal("must find m=" INTPTR_FORMAT "on om_in_use_list=" INTPTR_FORMAT,
L25: p2i(m), p2i(om_unlock(anchor);
L25: anchor = mid;
L26: }
L27: }
L28: }
L29: Atomic::dec(&self->om_in_use_listcount));
L26L30: }
L27: om_lockunlock(mid);
L28: next = unmarked_next(mid);
L29: }
L30: L31: }
L31L32: prepend_to_om_free_list(self, m);
...
The last line of the code block (L31L32) prepends 'm' to self's free list.
...
L01: int ObjectSynchronizer::deflate_monitor_list(ObjectMonitor** list_p,
L02: int* count_p,
L03: ObjectMonitor** free_head_p,
L04: ObjectMonitor** free_tail_p) {
L05: ObjectMonitor* cur_mid_in_use = NULL;
L06: ObjectMonitor* mid = NULL;
L07: ObjectMonitor* next = NULL;
L08: int deflated_count = 0;
L09: if ((mid = get_list_head_locked(list_p)) == NULL) {
L10: return 0; // The list is empty so nothing to deflate.
L11: }
L12: next = unmarked_next(mid);
L13: while (true) {
L14: oop obj = (oop) mid->object();
L15: if (obj != NULL && deflate_monitor(mid, obj, free_head_p, free_tail_p)) {
L16: if (cur_mid_in_use == NULL) {
L17: Atomic::store(list_p, next);
L18: } else {
L19: set_next(cur_mid_in_use, ->set_next_om(next);
L20: }
L21: deflated_count++;
L22: Atomic::dec(count_p);
L23: setmid->set_next_om(mid, NULL);
L24: } else {
L25: om_unlock(mid);
L26: cur_mid_in_use = mid;
L27: }
L28: mid = next;
L29: if (mid == NULL) {
L30: break; // Reached end of the list so nothing more to deflate.
L31: }
L32: om_lock(mid);
L33: next = unmarked_next(mid);
L34: }
L35: return deflated_count;
L36: }
Note: The above version of deflate_monitor_list() uses locking, but those changes were dropped during the code review cycle for JDK-8235795. The locking is only needed when additional calls to audit_and_print_stats() are used during debugging so it was decided that the pushed version would be simpler.
The above is not an exact copy of the code block from deflate_monitor_list(), but it is the highlights. What the above code block needs to do is pretty simple:
...
ObjectSynchronizer::deflate_monitor_list_using_JT() is responsible for asynchronously deflating idle ObjectMonitors using a JavaThread. This function uses the more complicated lock-cur_mid_in_use-and-mid-as-we-go protocol because om_release() can do list deletions in parallel. We also lock-next-next-as-we-go to prevent an om_flush() that is behind this thread from passing us. Because this function can asynchronously interact with so many other functions, this is the largest clip of code:
L01: int ObjectSynchronizer::deflate_monitor_list_using_JT(ObjectMonitor** list_p,
L02: int* count_p,
L03: ObjectMonitor** free_head_p,
L04: ObjectMonitor** free_tail_p,
L05: ObjectMonitor** saved_mid_in_use_p) {
L06: JavaThread* self = JavaThread::current();
L07: ObjectMonitor* cur_mid_in_use = NULL;
L08: ObjectMonitor* mid = NULL;
L09: ObjectMonitor* next = NULL;
L10: ObjectMonitor* next_next = NULL;
L11: int deflated_count = 0;
L12: NoSafepointVerifier nsv;
L13: if (*saved_mid_in_use_p == NULL) {
L13L14: if ((mid = get_list_head_locked(list_p)) == NULL) {
L14L15: return 0; // The list is empty so nothing to deflate.
L15L16: }
L16L17: next = unmarked_next(mid);
L17L18: } else {
L18L19: cur_mid_in_use = *saved_mid_in_use_p;
L19L20: om_lock(cur_mid_in_use);
L20L21: mid = unmarked_next(cur_mid_in_use);
L21L22: if (mid == NULL) {
L22L23: om_unlock(cur_mid_in_use);
L23L24: *saved_mid_in_use_p = NULL;
L24L25: return 0; // The remainder is empty so nothing more to deflate.
L25L26: }
L26L27: om_lock(mid);
L27L28: next = unmarked_next(mid);
L28L29: }
L29L30: while (true) {
L30L31: if (next != NULL) {
L31L32: om_lock(next);
L32L33: next_next = unmarked_next(next);
L33L34: }
L34L35: if (mid->object() != NULL && mid->is_old() &&
L35L36: deflate_monitor_using_JT(mid, free_head_p, free_tail_p)) {
L36L37: if (cur_mid_in_use == NULL) {
L37L38: Atomic::store(list_p, next);
L38L39: } else {
L39L40: ObjectMonitor* locked_next = mark_om_ptr(next);
L40L41: set_next(cur_mid_in_use, ->set_next_om(locked_next);
L41L42: }
L42L43: deflated_count++;
L43L44: Atomic::dec(count_p);
L44L45: setmid->set_next_om(mid, NULL);
L45L46: mid = next; // mid keeps non-NULL next's locked state
L46L47: next = next_next;
L47L48: } else {
L48L49: if (cur_mid_in_use != NULL) {
L49L50: om_unlock(cur_mid_in_use);
L50L51: }
L51L52: cur_mid_in_use = mid;
L52L53: mid = next; // mid keeps non-NULL next's locked state
L53L54: next = next_next;
L54L55: if (SafepointMechanism::should_block(self) &&
L55L56: cur_mid_in_use != Atomic::load(list_p) && cur_mid_in_use->is_old()) {
L56L57: *saved_mid_in_use_p = cur_mid_in_use;
L57L58: om_unlock(cur_mid_in_use);
L58L59: if (mid != NULL) {
L59L60: om_unlock(mid);
L60L61: }
L61L62: return deflated_count;
L62L63: }
L63L64: }
L64L65: if (mid == NULL) {
L65L66: if (cur_mid_in_use != NULL) {
L66L67: om_unlock(cur_mid_in_use);
L67L68: }
L68L69: break; // Reached end of the list so nothing more to deflate.
L69L70: }
L70L71: }
L71L72: *saved_mid_in_use_p = NULL;
L72L73: return deflated_count;
L73L74: }
The above is not an exact copy of the code block from deflate_monitor_list_using_JT(), but it is the highlights. What the above code block needs to do is pretty simple:
...
Since we're using the more complicated lock-cur_mid_in_use-and-mid-as-we-go protocol and also the lock-next-next-as-we-go protocol, there is a mind numbing amount of detail:
...
ObjectSynchronizer::deflate_idle_monitors() handles deflating idle monitors at a safepoint from the global in-use list using ObjectSynchronizer::deflate_monitor_list(). There are only a few things that are worth mentioning:
...
ObjectSynchronizer::deflate_common_idle_monitors_using_JT() handles asynchronously deflating idle monitors from either the global in-use list or a per-thread in-use list using ObjectSynchronizer::deflate_monitor_list_using_JT(). There are only a few things that are worth mentioning:
...
...
((LVarsom_list_globals.population - LVarsom_list_globals.free_count) / LVarsom_list_globals.population) > NN%
(LVars.population - LVars.free_count) > MonitorBound
...
...
Other invocation changes by the Async Monitor Deflation project (when async deflation is enabled):
VM_Exit::doit_prologue() will request a special cleanup to reduce the noise in 'monitorinflation' logging at VM exit time.
Before the final safepoint in a non-System.exit() end to the VM, we will request a special cleanup to reduce the noise in 'monitorinflation' logging at VM exit time.
WB_G1StartMarkCycle()