...
T-hash ObjectMonitor T-deflate
---------------------- +-----------------------+ --------------------------------------
save_om_ptr() { | owner=NULL | deflate_monitor_using_JT() {
: | contentions=0 | cmpxchg(DEFLATER_MARKER, &owner, NULL)
: | count=0 |
atomic inc ref_count | ref_count=0 |
+-----------------------+
...
T-hash ObjectMonitor T-deflate
---------------------- +-----------------------+ ------------------------------------------
save_om_ptr() { | owner=DEFLATER_MARKER | cmpxchg(DEFLATER_MARKER, &owner, NULL) deflate_monitor_using_JT() {
: | countcontentions=0 | if (waiters != 0 or ref_count != 0) {cmpxchg(DEFLATER_MARKER, &owner, NULL)
atomic inc ref_count | ref_count=0 | } if (waiters != 0 || ref_count != 0) {
+-----------------------+ }
prev = cmpxchg(-max_jint, &countcontentions, 0)
- T-deflate has set the owner field to DEFLATER_MARKER.
- T-deflate is about to check the waiters and ref_count fields.
- T-hash is about to inc the ref_count field.
...
T-hash ObjectMonitor T-deflate
------------------------ +-----------------------+ --------------------------------------------
save_om_ptr() { | owner=DEFLATER_MARKER | cmpxchg(DEFLATER_MARKER, &owner, NULL)deflate_monitor_using_JT() {
atomic inc ref_count | countcontentions=-max_jint jint | cmpxchg(DEFLATER_MARKER, &owner, NULL)
1> if (waitersowner !== 0 or| ref_count=1 !=| 0) {
if (ownerwaiters !== 0 || ref_count !=1 | 0) }{
DEFLATER_MARKER) {&& +-----------------------+ prev = cmpxchg(- }
contentions <= 0) { || prev = cmpxchg(-max_jint, &countcontentions, 0)
atomic dec ref_count || count \/ 1> if (prev == 0 &&
2> return false to to \/ owner == DEFLATER_MARKER) {
cause a retry +-----------------------+ restore object header
} |owner owner== DEFLATER_MARKER) {
| finish the deflation
| count cause a retry | owner=DEFLATER_MARKER | restore obj header
} | contentions=-max_jint jint | } 2> finish the deflation
| ref_count=0 | }
+-----------------------+
- T-deflate made it past the first ref_count check before T-hash incremented it.
- T-deflate set the count contentions field to -max_jint and T-enter incremented the ref_count field.
- The first ObjectMonitor box is showing the fields at this point and the "1>" markers are showing where each thread is at for that ObjectMonitor box.
- T-deflate has won the race so it restores object obj header (not shown) and finishes the deflation.
- T-hash observes "owner == DEFLATER_MARKER && contentions <= 0" so it decrements ref_count and returns false to cause a retry.
- The second ObjectMonitor box is showing the fields at this point and the "2>" markers are showing where each thread is at for that ObjectMonitor box.
- When T-hash retries:
- if it observes "owner == DEFLATER_MARKER && contentions <= 0" it will retry again.
- if it observes the restored object header:
- if the object's header does not have a hash, then generate a hash and merge it with the object's header.
- extract the hash from the object's header and return it.
...
T-hash ObjectMonitor T-deflate
------------------------ +-----------------------+ ------------------------------------------
save_om_ptr() { | header=dmw_no_hash | cmpxchg(DEFLATER_MARKER, &owner, NULL) deflate_monitor_using_JT() {
atomic inc ref_count | owner=DEFLATER_MARKER | if (waiters != 0 or ref_count != 0) {
cmpxchg(DEFLATER_MARKER, &owner, NULL)
1> if (owner == | countcontentions=0 | 1> if cmpxchg(NULL, &owner, DEFLATER_MARKER)waiters != 0 || ref_count != 0) {
DEFLATER_MARKER) { && | ref_count=1 | bailout on deflation
} cmpxchg(NULL, &owner, DEFLATER_MARKER)
contentions <= 0) { +-----------------------+ 2> bailout on deflation
} || }
if (object no longer || prev = cmpxchg(-max_jint, &count, 0)
has a monitor orlonger \/ prev = cmpxchg(-max_jint, \/&contentions, 0)
ishas a differentmonitor or +-----------------------+
monitor) { is a different | header=dmw_no_hash |
atomic dec ref_countmonitor) { | owner=NULL | owner=NULL |
return false to |
count=0atomic dec ref_count | contentions=0 |
return causefalse ato retry | ref_count=1 |
} cause a retry +-----------------------+
save} om_ptr in the ||
ObjectMonitorHandle \/
}||
2> save om_ptr in the \/
ObjectMonitorHandle +-----------------------+
} if save_om_ptr() { | header=dmw_hash |
if no hash save_om_ptr() { | owner=NULL |
if no genhash hash & merge | count=0 | contentions=0 |
gen hash = hash(header)& merge | ref_count=1 |
} hash = hash(header) +-----------------------+
}
3> atomic dec ref_count
return hash
- T-hash has incremented ref_count before T-deflate made it past that check.
- The first ObjectMonitor box is showing the fields at this point and the "1>" markers are showing where each thread is at for that ObjectMonitor box.
- T-deflate bails out on deflation, but first it tries to restore the owner field.
- The return value of cmpxchg() is not checked here.
- If T-deflate cannot restore the owner field to NULL, then another thread has managed to enter the monitor (or enter and exit the monitor) and we don't want to overwrite that information.
- T-hash observes: !
- == DEFLATER_MARKER && contentions == 0" or
- "owner == NULL && contentions == 0" so it does not cause a retry.
- T-hash verifies that the object still has a monitor and that monitor still refers to our current ObjectMonitor.
- The second ObjectMonitor box is showing the fields at this point and the "2>" markers are showing where each thread is at for that ObjectMonitor box.
- if save_om_ptr() returned returns true ObjectMonitor is safe
- if ObjectMonitor's 'header/dmw' field does not have a hash, then generate a hash and merge it with the 'header/dmw' field.
- extract the hash from the ObjectMonitor's 'header/dmw' field.
- The third ObjectMonitor box is showing the fields at this point and the "3>" marker is showing where T-hash is at for that ObjectMonitor box.
- T-hash decrements the ref_count field.
- T-hash returns the hash value.
...
- Counterpart function mapping for those that know the existing code:
- ObjectSynchronizer class:
- deflate_idle_monitors() has deflate_global_idle_monitors_using_JT() and , deflate_per_thread_idle_monitors_using_JT(), and deflate_common_idle_monitors_using_JT().
- deflate_monitor_list() has deflate_monitor_list_using_JT()
- deflate_monitor() has deflate_monitor_using_JT()
- ObjectMonitor class:
- is_busy() has is_busy_async()
- clear() has clear_using_JT()
- These functions recognize the Async Monitor Deflation protocol and adapt their operations:
- ObjectMonitor::enter()
- ObjectMonitor::EnterI()
- ObjectMonitor::ReenterI()
- most callers to enter() had to indirectly adapt to the protocol and retry their operations.
- Also these functions had to adapt and retry their operations:
- ObjectSynchronizer::quick_enter()
- ObjectSynchronizer::slow_enter()
- ObjectSynchronizer::reenter()
- ObjectSynchronizer::jni_enter()
- ObjectSynchronizer::FastHashCode()
- ObjectSynchronizer::current_thread_holds_lock()
- ObjectSynchronizer::query_lock_ownership()
- ObjectSynchronizer::get_lock_owner()
- ObjectSynchronizer::monitors_iterate()
- ObjectSynchronizer::inflate_helper()
- ObjectSynchronizer::inflate()
- Various assertions had to be modified to pass without their real check when AsyncDeflateIdleMonitors is true; this is due to the change in semantics for the ObjectMonitor owner and count fields.
- ObjectMonitor has a new allocation_state field that supports three states: 'Free', 'New', 'Old'. Async Monitor Deflation is only applied to ObjectMonitors that have reached the 'Old' state. When the Async Monitor Deflation code sees an ObjectMonitor in the 'New' state, it is changed to the 'Old' state, but is not deflated. This prevents a newly allocated ObjectMonitor from being immediately deflated which could cause an inflation<->deflation oscillation.
- ObjectMonitor has a new ref_count field that is used to indicate that an ObjectMonitor* is in use so the ObjectMonitor should not be deflated; this is needed for operations on non-busy monitors so that ObjectMonitor values don't change while they are being queried. There is a new ObjectMonitorHandle helper to manage the ref_count.
- The ObjectMonitor::owner() accessor detects DEFLATER_MARKER and returns NULL in that case to minimize the places that need to understand the new DEFLATER_MARKER value.
- System.gc()/JVM_GC() causes a special monitor list cleanup request which uses the safepoint based monitor list mechanism. So even if AsyncDeflateIdleMonitors is enabled, the safepoint based mechanism is still used by this special case.
- This is necessary for those tests that do something to cause an object's monitor to be inflated, clear the only reference to the object and then expect that enough System.gc() calls will eventually cause the object to be GC'ed even when the thread never inflates another object's monitor. Yes, we have several tests like that. :-)