8153224: Monitor deflation prolong safepoints
Add support for AsyncDeflateIdleMonitors (default true); the async deflation work is performed by the ServiceThread. Co-authored-by: Carsten Varming <varming@gmail.com> Reviewed-by: dcubed, rehn, rkennke, cvarming, coleenp, acorn, dholmes, eosterlund
This commit is contained in:
parent
30aa1b0689
commit
00f223e22f
@ -74,6 +74,7 @@
|
||||
#include "runtime/os.inline.hpp"
|
||||
#include "runtime/perfData.hpp"
|
||||
#include "runtime/reflection.hpp"
|
||||
#include "runtime/synchronizer.hpp"
|
||||
#include "runtime/thread.inline.hpp"
|
||||
#include "runtime/threadSMR.hpp"
|
||||
#include "runtime/vframe.inline.hpp"
|
||||
@ -490,6 +491,11 @@ JVM_END
|
||||
JVM_ENTRY_NO_ENV(void, JVM_GC(void))
|
||||
JVMWrapper("JVM_GC");
|
||||
if (!DisableExplicitGC) {
|
||||
if (AsyncDeflateIdleMonitors) {
|
||||
// AsyncDeflateIdleMonitors needs to know when System.gc() is
|
||||
// called so any special deflation can be done at a safepoint.
|
||||
ObjectSynchronizer::set_is_special_deflation_requested(true);
|
||||
}
|
||||
Universe::heap()->collect(GCCause::_java_lang_system_gc);
|
||||
}
|
||||
JVM_END
|
||||
|
@ -653,6 +653,9 @@ JvmtiEnvBase::get_current_contended_monitor(JavaThread *java_thread, jobject *mo
|
||||
current_jt == java_thread->active_handshaker(),
|
||||
"call by myself or at direct handshake");
|
||||
oop obj = NULL;
|
||||
// The ObjectMonitor* can't be async deflated since we are either
|
||||
// at a safepoint or the calling thread is operating on itself so
|
||||
// it cannot leave the underlying wait()/enter() call.
|
||||
ObjectMonitor *mon = java_thread->current_waiting_monitor();
|
||||
if (mon == NULL) {
|
||||
// thread is not doing an Object.wait() call
|
||||
@ -730,7 +733,10 @@ JvmtiEnvBase::get_locked_objects_in_frame(JavaThread* calling_thread, JavaThread
|
||||
HandleMark hm;
|
||||
oop wait_obj = NULL;
|
||||
{
|
||||
// save object of current wait() call (if any) for later comparison
|
||||
// The ObjectMonitor* can't be async deflated since we are either
|
||||
// at a safepoint or the calling thread is operating on itself so
|
||||
// it cannot leave the underlying wait() call.
|
||||
// Save object of current wait() call (if any) for later comparison.
|
||||
ObjectMonitor *mon = java_thread->current_waiting_monitor();
|
||||
if (mon != NULL) {
|
||||
wait_obj = (oop)mon->object();
|
||||
@ -738,7 +744,10 @@ JvmtiEnvBase::get_locked_objects_in_frame(JavaThread* calling_thread, JavaThread
|
||||
}
|
||||
oop pending_obj = NULL;
|
||||
{
|
||||
// save object of current enter() call (if any) for later comparison
|
||||
// The ObjectMonitor* can't be async deflated since we are either
|
||||
// at a safepoint or the calling thread is operating on itself so
|
||||
// it cannot leave the underlying enter() call.
|
||||
// Save object of current enter() call (if any) for later comparison.
|
||||
ObjectMonitor *mon = java_thread->current_pending_monitor();
|
||||
if (mon != NULL) {
|
||||
pending_obj = (oop)mon->object();
|
||||
|
@ -73,6 +73,7 @@
|
||||
#include "runtime/jniHandles.inline.hpp"
|
||||
#include "runtime/os.hpp"
|
||||
#include "runtime/sweeper.hpp"
|
||||
#include "runtime/synchronizer.hpp"
|
||||
#include "runtime/thread.hpp"
|
||||
#include "runtime/threadSMR.hpp"
|
||||
#include "runtime/vm_version.hpp"
|
||||
@ -477,6 +478,12 @@ WB_END
|
||||
|
||||
WB_ENTRY(jboolean, WB_G1StartMarkCycle(JNIEnv* env, jobject o))
|
||||
if (UseG1GC) {
|
||||
if (AsyncDeflateIdleMonitors) {
|
||||
// AsyncDeflateIdleMonitors needs to know when System.gc() or
|
||||
// the equivalent is called so any special clean up can be done
|
||||
// at a safepoint, e.g., TestHumongousClassLoader.java.
|
||||
ObjectSynchronizer::set_is_special_deflation_requested(true);
|
||||
}
|
||||
G1CollectedHeap* g1h = G1CollectedHeap::heap();
|
||||
if (!g1h->concurrent_mark()->cm_thread()->during_cycle()) {
|
||||
g1h->collect(GCCause::_wb_conc_mark);
|
||||
@ -1448,6 +1455,12 @@ WB_ENTRY(jboolean, WB_IsInStringTable(JNIEnv* env, jobject o, jstring javaString
|
||||
WB_END
|
||||
|
||||
WB_ENTRY(void, WB_FullGC(JNIEnv* env, jobject o))
|
||||
if (AsyncDeflateIdleMonitors) {
|
||||
// AsyncDeflateIdleMonitors needs to know when System.gc() or
|
||||
// the equivalent is called so any special clean up can be done
|
||||
// at a safepoint, e.g., TestHumongousClassLoader.java.
|
||||
ObjectSynchronizer::set_is_special_deflation_requested(true);
|
||||
}
|
||||
Universe::heap()->soft_ref_policy()->set_should_clear_all_soft_refs(true);
|
||||
Universe::heap()->collect(GCCause::_wb_full_gc);
|
||||
#if INCLUDE_G1GC
|
||||
@ -1797,6 +1810,13 @@ WB_ENTRY(jboolean, WB_IsMonitorInflated(JNIEnv* env, jobject wb, jobject obj))
|
||||
WB_END
|
||||
|
||||
WB_ENTRY(void, WB_ForceSafepoint(JNIEnv* env, jobject wb))
|
||||
if (AsyncDeflateIdleMonitors) {
|
||||
// AsyncDeflateIdleMonitors needs to know when System.gc() or
|
||||
// the equivalent is called so any special clean up can be done
|
||||
// at a safepoint, e.g., TestRTMTotalCountIncrRate.java or
|
||||
// TestUseRTMForStackLocks.java.
|
||||
ObjectSynchronizer::set_is_special_deflation_requested(true);
|
||||
}
|
||||
VM_ForceSafepoint force_safepoint_op;
|
||||
VMThread::execute(&force_safepoint_op);
|
||||
WB_END
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -36,24 +36,24 @@ void BasicLock::print_on(outputStream* st) const {
|
||||
void BasicLock::move_to(oop obj, BasicLock* dest) {
|
||||
// Check to see if we need to inflate the lock. This is only needed
|
||||
// if an object is locked using "this" lightweight monitor. In that
|
||||
// case, the displaced_header() is unlocked, because the
|
||||
// case, the displaced_header() is unlocked/is_neutral, because the
|
||||
// displaced_header() contains the header for the originally unlocked
|
||||
// object. However the object could have already been inflated. But it
|
||||
// does not matter, the inflation will just a no-op. For other cases,
|
||||
// object. However the lock could have already been inflated. But it
|
||||
// does not matter, this inflation will just a no-op. For other cases,
|
||||
// the displaced header will be either 0x0 or 0x3, which are location
|
||||
// independent, therefore the BasicLock is free to move.
|
||||
//
|
||||
// During OSR we may need to relocate a BasicLock (which contains a
|
||||
// displaced word) from a location in an interpreter frame to a
|
||||
// new location in a compiled frame. "this" refers to the source
|
||||
// basiclock in the interpreter frame. "dest" refers to the destination
|
||||
// basiclock in the new compiled frame. We *always* inflate in move_to().
|
||||
// The always-Inflate policy works properly, but in 1.5.0 it can sometimes
|
||||
// cause performance problems in code that makes heavy use of a small # of
|
||||
// uncontended locks. (We'd inflate during OSR, and then sync performance
|
||||
// would subsequently plummet because the thread would be forced thru the slow-path).
|
||||
// This problem has been made largely moot on IA32 by inlining the inflated fast-path
|
||||
// operations in Fast_Lock and Fast_Unlock in i486.ad.
|
||||
// BasicLock in the interpreter frame. "dest" refers to the destination
|
||||
// BasicLock in the new compiled frame. We *always* inflate in move_to()
|
||||
// when the object is locked using "this" lightweight monitor.
|
||||
//
|
||||
// The always-Inflate policy works properly, but it depends on the
|
||||
// inflated fast-path operations in fast_lock and fast_unlock to avoid
|
||||
// performance problems. See x86/macroAssembler_x86.cpp: fast_lock()
|
||||
// and fast_unlock() for examples.
|
||||
//
|
||||
// Note that there is a way to safely swing the object's markword from
|
||||
// one stack location to another. This avoids inflation. Obviously,
|
||||
@ -63,8 +63,10 @@ void BasicLock::move_to(oop obj, BasicLock* dest) {
|
||||
// we'll leave that optimization for another time.
|
||||
|
||||
if (displaced_header().is_neutral()) {
|
||||
// The object is locked and the resulting ObjectMonitor* will also be
|
||||
// locked so it can't be async deflated until ownership is dropped.
|
||||
ObjectSynchronizer::inflate_helper(obj);
|
||||
// WARNING: We can not put check here, because the inflation
|
||||
// WARNING: We cannot put a check here, because the inflation
|
||||
// will not update the displaced header. Once BasicLock is inflated,
|
||||
// no one should ever look at its content.
|
||||
} else {
|
||||
|
@ -683,11 +683,21 @@ const size_t minimumSymbolTableSize = 1024;
|
||||
"Disable the use of stack guard pages if the JVM is loaded " \
|
||||
"on the primordial process thread") \
|
||||
\
|
||||
diagnostic(bool, AsyncDeflateIdleMonitors, true, \
|
||||
"Deflate idle monitors using the ServiceThread.") \
|
||||
\
|
||||
/* notice: the max range value here is max_jint, not max_intx */ \
|
||||
/* because of overflow issue */ \
|
||||
diagnostic(intx, AsyncDeflationInterval, 250, \
|
||||
"Async deflate idle monitors every so many milliseconds when " \
|
||||
"MonitorUsedDeflationThreshold is exceeded (0 is off).") \
|
||||
range(0, max_jint) \
|
||||
\
|
||||
experimental(intx, MonitorUsedDeflationThreshold, 90, \
|
||||
"Percentage of used monitors before triggering cleanup " \
|
||||
"safepoint which deflates monitors (0 is off). " \
|
||||
"The check is performed on GuaranteedSafepointInterval.") \
|
||||
range(0, 100) \
|
||||
"Percentage of used monitors before triggering deflation (0 is " \
|
||||
"off). The check is performed on GuaranteedSafepointInterval " \
|
||||
"or AsyncDeflationInterval.") \
|
||||
range(0, 100) \
|
||||
\
|
||||
experimental(intx, hashCode, 5, \
|
||||
"(Unstable) select hashCode generation algorithm") \
|
||||
|
@ -172,8 +172,12 @@ void exit_globals() {
|
||||
if (log_is_enabled(Info, monitorinflation)) {
|
||||
// The ObjectMonitor subsystem uses perf counters so
|
||||
// do this before perfMemory_exit().
|
||||
// ObjectSynchronizer::finish_deflate_idle_monitors()'s call
|
||||
// to audit_and_print_stats() is done at the Debug level.
|
||||
// These other two audit_and_print_stats() calls are done at the
|
||||
// Debug level at a safepoint:
|
||||
// - for safepoint based deflation auditing:
|
||||
// ObjectSynchronizer::finish_deflate_idle_monitors()
|
||||
// - for async deflation auditing:
|
||||
// ObjectSynchronizer::do_safepoint_work()
|
||||
ObjectSynchronizer::audit_and_print_stats(true /* on_exit */);
|
||||
}
|
||||
perfMemory_exit();
|
||||
|
@ -240,7 +240,7 @@ void ObjectMonitor::operator delete[] (void *p) {
|
||||
// -----------------------------------------------------------------------------
|
||||
// Enter support
|
||||
|
||||
void ObjectMonitor::enter(TRAPS) {
|
||||
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;
|
||||
@ -248,20 +248,20 @@ void ObjectMonitor::enter(TRAPS) {
|
||||
void* cur = try_set_owner_from(NULL, Self);
|
||||
if (cur == NULL) {
|
||||
assert(_recursions == 0, "invariant");
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cur == Self) {
|
||||
// TODO-FIXME: check for integer overflow! BUGID 6557169.
|
||||
_recursions++;
|
||||
return;
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
|
||||
// We've encountered genuine contention.
|
||||
@ -281,7 +281,7 @@ void ObjectMonitor::enter(TRAPS) {
|
||||
", encoded this=" INTPTR_FORMAT, ((oop)object())->mark().value(),
|
||||
markWord::encode(this).value());
|
||||
Self->_Stalled = 0;
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
assert(_owner != Self, "invariant");
|
||||
@ -290,12 +290,25 @@ void ObjectMonitor::enter(TRAPS) {
|
||||
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");
|
||||
assert(AsyncDeflateIdleMonitors || this->object() != NULL, "invariant");
|
||||
assert(AsyncDeflateIdleMonitors || contentions() >= 0, "must not be negative: contentions=%d", contentions());
|
||||
|
||||
// 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);
|
||||
// Keep track of contention for JVM/TI and M&M queries.
|
||||
add_to_contentions(1);
|
||||
if (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;
|
||||
add_to_contentions(-1);
|
||||
return false;
|
||||
}
|
||||
|
||||
JFR_ONLY(JfrConditionalFlushWithStacktrace<EventJavaMonitorEnter> flush(jt);)
|
||||
EventJavaMonitorEnter event;
|
||||
@ -356,8 +369,8 @@ void ObjectMonitor::enter(TRAPS) {
|
||||
// acquire it.
|
||||
}
|
||||
|
||||
Atomic::dec(&_contentions);
|
||||
assert(_contentions >= 0, "invariant");
|
||||
add_to_contentions(-1);
|
||||
assert(contentions() >= 0, "must not be negative: contentions=%d", contentions());
|
||||
Self->_Stalled = 0;
|
||||
|
||||
// Must either set _recursions = 0 or ASSERT _recursions == 0.
|
||||
@ -393,6 +406,7 @@ void ObjectMonitor::enter(TRAPS) {
|
||||
event.commit();
|
||||
}
|
||||
OM_PERFDATA_OP(ContendedLockAttempts, inc());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Caveat: TryLock() is not necessarily serializing if it returns failure.
|
||||
@ -412,12 +426,86 @@ int ObjectMonitor::TryLock(Thread * Self) {
|
||||
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");
|
||||
|
||||
// Separate loads in is_being_async_deflated(), which is almost always
|
||||
// called before this function, from the load of dmw/header below.
|
||||
if (support_IRIW_for_not_multiple_copy_atomic_cpu) {
|
||||
// A non-multiple copy atomic (nMCA) machine needs a bigger
|
||||
// hammer to separate the loads before and the load below.
|
||||
OrderAccess::fence();
|
||||
} else {
|
||||
OrderAccess::loadload();
|
||||
}
|
||||
|
||||
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. More than one racing caller to this function
|
||||
// can rarely 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: contentions=%d, waiters=%d, owner=" INTPTR_FORMAT
|
||||
", cxq=" INTPTR_FORMAT ", EntryList=" INTPTR_FORMAT, _contentions,
|
||||
_waiters, p2i(_owner), p2i(_cxq), p2i(_EntryList));
|
||||
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();
|
||||
}
|
||||
|
||||
@ -436,6 +524,20 @@ void ObjectMonitor::EnterI(TRAPS) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (AsyncDeflateIdleMonitors &&
|
||||
try_set_owner_from(DEFLATER_MARKER, Self) == DEFLATER_MARKER) {
|
||||
// Cancelled the in-progress async deflation. We bump contentions an
|
||||
// extra time to prevent the async deflater thread from temporarily
|
||||
// changing it to -max_jint and back to zero (no flicker to confuse
|
||||
// is_being_async_deflated()). The async deflater thread will
|
||||
// decrement contentions after it recognizes that the async
|
||||
// deflation was cancelled.
|
||||
add_to_contentions(1);
|
||||
assert(_succ != Self, "invariant");
|
||||
assert(_Responsible != Self, "invariant");
|
||||
return;
|
||||
}
|
||||
|
||||
assert(InitDone, "Unexpectedly not initialized");
|
||||
|
||||
// We try one round of spinning *before* enqueueing Self.
|
||||
@ -552,6 +654,18 @@ void ObjectMonitor::EnterI(TRAPS) {
|
||||
|
||||
if (TryLock(Self) > 0) break;
|
||||
|
||||
if (AsyncDeflateIdleMonitors &&
|
||||
try_set_owner_from(DEFLATER_MARKER, Self) == DEFLATER_MARKER) {
|
||||
// Cancelled the in-progress async deflation. We bump contentions an
|
||||
// extra time to prevent the async deflater thread from temporarily
|
||||
// changing it to -max_jint and back to zero (no flicker to confuse
|
||||
// is_being_async_deflated()). The async deflater thread will
|
||||
// decrement contentions after it recognizes that the async
|
||||
// deflation was cancelled.
|
||||
add_to_contentions(1);
|
||||
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.
|
||||
@ -816,7 +930,7 @@ void ObjectMonitor::UnlinkAfterAcquire(Thread *Self, ObjectWaiter *SelfNode) {
|
||||
// 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,
|
||||
// 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
|
||||
@ -1091,7 +1205,7 @@ void ObjectMonitor::ExitEpilog(Thread * Self, ObjectWaiter * Wakee) {
|
||||
// out-of-scope (non-extant).
|
||||
Wakee = NULL;
|
||||
|
||||
// Drop the lock
|
||||
// Drop the lock.
|
||||
// Uses a fence to separate release_store(owner) from the LD in unpark().
|
||||
release_clear_owner(Self);
|
||||
OrderAccess::fence();
|
||||
@ -1139,16 +1253,19 @@ intx ObjectMonitor::complete_exit(TRAPS) {
|
||||
|
||||
// reenter() enters a lock and sets recursion count
|
||||
// complete_exit/reenter operate as a wait without waiting
|
||||
void ObjectMonitor::reenter(intx recursions, TRAPS) {
|
||||
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");
|
||||
enter(THREAD); // enter the monitor
|
||||
if (!enter(THREAD)) {
|
||||
return false;
|
||||
}
|
||||
// Entered the monitor.
|
||||
guarantee(_recursions == 0, "reenter recursion");
|
||||
_recursions = recursions;
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Checks that the current THREAD owns this monitor and causes an
|
||||
@ -1962,14 +2079,20 @@ void ObjectMonitor::print() const { print_on(tty); }
|
||||
// (ObjectMonitor) 0x00007fdfb6012e40 = {
|
||||
// _header = 0x0000000000000001
|
||||
// _object = 0x000000070ff45fd0
|
||||
// _next_om = 0x0000000000000000
|
||||
// _allocation_state = Old
|
||||
// _pad_buf0 = {
|
||||
// [0] = '\0'
|
||||
// ...
|
||||
// [103] = '\0'
|
||||
// [43] = '\0'
|
||||
// }
|
||||
// _owner = 0x0000000000000000
|
||||
// _previous_owner_tid = 0
|
||||
// _pad_buf1 = {
|
||||
// [0] = '\0'
|
||||
// ...
|
||||
// [47] = '\0'
|
||||
// }
|
||||
// _next_om = 0x0000000000000000
|
||||
// _recursions = 0
|
||||
// _EntryList = 0x0000000000000000
|
||||
// _cxq = 0x0000000000000000
|
||||
@ -1987,7 +2110,17 @@ 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(" _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(" ...");
|
||||
@ -1995,6 +2128,12 @@ void ObjectMonitor::print_debug_style_on(outputStream* st) const {
|
||||
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));
|
||||
@ -2002,7 +2141,7 @@ void ObjectMonitor::print_debug_style_on(outputStream* st) const {
|
||||
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(" _contentions = %d", contentions());
|
||||
st->print_cr(" _WaitSet = " INTPTR_FORMAT, p2i(_WaitSet));
|
||||
st->print_cr(" _waiters = %d", _waiters);
|
||||
st->print_cr(" _WaitSetLock = %d", _WaitSetLock);
|
||||
|
@ -136,13 +136,21 @@ class ObjectMonitor {
|
||||
// Enforced by the assert() in header_addr().
|
||||
volatile markWord _header; // displaced object header word - mark
|
||||
void* volatile _object; // backward object pointer - strong root
|
||||
private:
|
||||
typedef enum {
|
||||
Free = 0, // Free must be 0 for monitor to be free after memset(..,0,..).
|
||||
New,
|
||||
Old
|
||||
} AllocationState;
|
||||
AllocationState _allocation_state;
|
||||
// Separate _header and _owner on different cache lines since both can
|
||||
// have busy multi-threaded access. _header and _object are set at
|
||||
// initial inflation and _object doesn't change until deflation so
|
||||
// _object is a good choice to share the cache line with _header.
|
||||
DEFINE_PAD_MINUS_SIZE(0, OM_CACHE_LINE_SIZE,
|
||||
sizeof(volatile markWord) + sizeof(void* volatile));
|
||||
// have busy multi-threaded access. _header, _object and _allocation_state
|
||||
// are set at initial inflation. _object and _allocation_state don't
|
||||
// change until deflation so _object and _allocation_state are good
|
||||
// choices to share the cache line with _header.
|
||||
DEFINE_PAD_MINUS_SIZE(0, OM_CACHE_LINE_SIZE, sizeof(volatile markWord) +
|
||||
sizeof(void* volatile) + sizeof(AllocationState));
|
||||
// Used by async deflation as a marker in the _owner field:
|
||||
#define DEFLATER_MARKER reinterpret_cast<void*>(-1)
|
||||
void* volatile _owner; // pointer to owning thread OR BasicLock
|
||||
volatile jlong _previous_owner_tid; // thread id of the previous owner of the monitor
|
||||
// Separate _owner and _next_om on different cache lines since
|
||||
@ -164,9 +172,10 @@ class ObjectMonitor {
|
||||
volatile int _Spinner; // for exit->spinner handoff optimization
|
||||
volatile int _SpinDuration;
|
||||
|
||||
volatile jint _contentions; // Number of active contentions in enter(). It is used by is_busy()
|
||||
jint _contentions; // Number of active contentions in enter(). It is used by is_busy()
|
||||
// along with other fields to determine if an ObjectMonitor can be
|
||||
// deflated. See ObjectSynchronizer::deflate_monitor().
|
||||
// deflated. It is also used by the async deflation protocol. See
|
||||
// ObjectSynchronizer::deflate_monitor() and deflate_monitor_using_JT().
|
||||
protected:
|
||||
ObjectWaiter* volatile _WaitSet; // LL of threads wait()ing on the monitor
|
||||
volatile jint _waiters; // number of waiting threads
|
||||
@ -233,17 +242,34 @@ class ObjectMonitor {
|
||||
|
||||
intptr_t is_busy() const {
|
||||
// TODO-FIXME: assert _owner == null implies _recursions = 0
|
||||
return _contentions|_waiters|intptr_t(_owner)|intptr_t(_cxq)|intptr_t(_EntryList);
|
||||
intptr_t ret_code = _waiters | intptr_t(_cxq) | intptr_t(_EntryList);
|
||||
if (!AsyncDeflateIdleMonitors) {
|
||||
ret_code |= contentions() | intptr_t(_owner);
|
||||
} else {
|
||||
if (contentions() > 0) {
|
||||
ret_code |= contentions();
|
||||
}
|
||||
if (_owner != DEFLATER_MARKER) {
|
||||
ret_code |= intptr_t(_owner);
|
||||
}
|
||||
}
|
||||
return ret_code;
|
||||
}
|
||||
const char* is_busy_to_string(stringStream* ss);
|
||||
|
||||
intptr_t is_entered(Thread* current) const;
|
||||
|
||||
void* owner() const;
|
||||
void* owner() const; // Returns NULL if DEFLATER_MARKER is observed.
|
||||
// Returns true if owner field == DEFLATER_MARKER and false otherwise.
|
||||
bool owner_is_DEFLATER_MARKER();
|
||||
// Returns true if 'this' is being async deflated and false otherwise.
|
||||
bool is_being_async_deflated();
|
||||
// Clear _owner field; current value must match old_value.
|
||||
void release_clear_owner(void* old_value);
|
||||
// Simply set _owner field to new_value; current value must match old_value.
|
||||
void set_owner_from(void* old_value, void* new_value);
|
||||
// Simply set _owner field to new_value; current value must match old_value1 or old_value2.
|
||||
void set_owner_from(void* old_value1, void* old_value2, void* new_value);
|
||||
// Simply set _owner field to self; current value must match basic_lock_p.
|
||||
void set_owner_from_BasicLock(void* basic_lock_p, Thread* self);
|
||||
// Try to set _owner field to new_value if the current value matches
|
||||
@ -262,6 +288,7 @@ class ObjectMonitor {
|
||||
jint waiters() const;
|
||||
|
||||
jint contentions() const;
|
||||
void add_to_contentions(jint value);
|
||||
intx recursions() const { return _recursions; }
|
||||
|
||||
// JVM/TI GetObjectMonitorUsage() needs this:
|
||||
@ -286,7 +313,9 @@ class ObjectMonitor {
|
||||
// _cxq == 0 _succ == NULL _owner == NULL _waiters == 0
|
||||
// _contentions == 0 EntryList == NULL
|
||||
// _recursions == 0 _WaitSet == NULL
|
||||
DEBUG_ONLY(stringStream ss;)
|
||||
#ifdef ASSERT
|
||||
stringStream ss;
|
||||
#endif
|
||||
assert((is_busy() | _recursions) == 0, "freeing in-use monitor: %s, "
|
||||
"recursions=" INTX_FORMAT, is_busy_to_string(&ss), _recursions);
|
||||
_succ = NULL;
|
||||
@ -301,13 +330,19 @@ class ObjectMonitor {
|
||||
void* object() const;
|
||||
void* object_addr();
|
||||
void set_object(void* obj);
|
||||
void set_allocation_state(AllocationState s);
|
||||
AllocationState allocation_state() const;
|
||||
bool is_free() const;
|
||||
bool is_old() const;
|
||||
bool is_new() const;
|
||||
|
||||
// Returns true if the specified thread owns the ObjectMonitor. Otherwise
|
||||
// returns false and throws IllegalMonitorStateException (IMSE).
|
||||
bool check_owner(Thread* THREAD);
|
||||
void clear();
|
||||
void clear_common();
|
||||
|
||||
void enter(TRAPS);
|
||||
bool enter(TRAPS);
|
||||
void exit(bool not_suspended, TRAPS);
|
||||
void wait(jlong millis, bool interruptable, TRAPS);
|
||||
void notify(TRAPS);
|
||||
@ -321,7 +356,7 @@ class ObjectMonitor {
|
||||
|
||||
// Use the following at your own risk
|
||||
intx complete_exit(TRAPS);
|
||||
void reenter(intx recursions, TRAPS);
|
||||
bool reenter(intx recursions, TRAPS);
|
||||
|
||||
private:
|
||||
void AddWaiter(ObjectWaiter* waiter);
|
||||
@ -332,10 +367,22 @@ class ObjectMonitor {
|
||||
void ReenterI(Thread* self, ObjectWaiter* self_node);
|
||||
void UnlinkAfterAcquire(Thread* self, ObjectWaiter* self_node);
|
||||
int TryLock(Thread* self);
|
||||
int NotRunnable(Thread* self, Thread * Owner);
|
||||
int NotRunnable(Thread* self, Thread* Owner);
|
||||
int TrySpin(Thread* self);
|
||||
void ExitEpilog(Thread* self, ObjectWaiter* Wakee);
|
||||
bool ExitSuspendEquivalent(JavaThread* self);
|
||||
void install_displaced_markword_in_object(const oop obj);
|
||||
};
|
||||
|
||||
// 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
|
||||
|
@ -52,19 +52,51 @@ inline jint ObjectMonitor::waiters() const {
|
||||
return _waiters;
|
||||
}
|
||||
|
||||
// Returns NULL if DEFLATER_MARKER is observed.
|
||||
inline void* ObjectMonitor::owner() const {
|
||||
return _owner;
|
||||
void* owner = _owner;
|
||||
return owner != DEFLATER_MARKER ? owner : NULL;
|
||||
}
|
||||
|
||||
// Returns true if owner field == DEFLATER_MARKER and false otherwise.
|
||||
// This accessor is called when we really need to know if the owner
|
||||
// field == DEFLATER_MARKER and any non-NULL value won't do the trick.
|
||||
inline bool ObjectMonitor::owner_is_DEFLATER_MARKER() {
|
||||
return Atomic::load(&_owner) == DEFLATER_MARKER;
|
||||
}
|
||||
|
||||
// Returns true if 'this' is being async deflated and false otherwise.
|
||||
inline bool ObjectMonitor::is_being_async_deflated() {
|
||||
return AsyncDeflateIdleMonitors && contentions() < 0;
|
||||
}
|
||||
|
||||
inline void ObjectMonitor::clear() {
|
||||
assert(Atomic::load(&_header).value() != 0, "must be non-zero");
|
||||
assert(_contentions == 0, "must be 0: contentions=%d", _contentions);
|
||||
assert(_waiters == 0, "must be 0: waiters=%d", _waiters);
|
||||
assert(_recursions == 0, "must be 0: recursions=" INTX_FORMAT, _recursions);
|
||||
assert(_object != NULL, "must be non-NULL");
|
||||
assert(_owner == NULL, "must be NULL: owner=" INTPTR_FORMAT, p2i(_owner));
|
||||
|
||||
Atomic::store(&_header, markWord::zero());
|
||||
|
||||
clear_common();
|
||||
}
|
||||
|
||||
inline void ObjectMonitor::clear_common() {
|
||||
if (AsyncDeflateIdleMonitors) {
|
||||
// Async deflation protocol uses the header, owner and contentions
|
||||
// fields. While the ObjectMonitor being deflated is on the global
|
||||
// free list, we leave those three fields alone; contentions < 0
|
||||
// will force any racing threads to retry. The header field is used
|
||||
// by install_displaced_markword_in_object() to restore the object's
|
||||
// header so we cannot check its value here.
|
||||
guarantee(_owner == NULL || _owner == DEFLATER_MARKER,
|
||||
"must be NULL or DEFLATER_MARKER: owner=" INTPTR_FORMAT,
|
||||
p2i(_owner));
|
||||
}
|
||||
assert(contentions() <= 0, "must not be positive: contentions=%d", contentions());
|
||||
assert(_waiters == 0, "must be 0: waiters=%d", _waiters);
|
||||
assert(_recursions == 0, "must be 0: recursions=" INTX_FORMAT, _recursions);
|
||||
assert(_object != NULL, "must be non-NULL");
|
||||
|
||||
set_allocation_state(Free);
|
||||
_object = NULL;
|
||||
}
|
||||
|
||||
@ -80,16 +112,21 @@ inline void ObjectMonitor::set_object(void* obj) {
|
||||
_object = obj;
|
||||
}
|
||||
|
||||
// return number of threads contending for this monitor
|
||||
// Return number of threads contending for this monitor.
|
||||
inline jint ObjectMonitor::contentions() const {
|
||||
return _contentions;
|
||||
return Atomic::load(&_contentions);
|
||||
}
|
||||
|
||||
// Add value to the contentions field.
|
||||
inline void ObjectMonitor::add_to_contentions(jint value) {
|
||||
Atomic::add(&_contentions, value);
|
||||
}
|
||||
|
||||
// Clear _owner field; current value must match old_value.
|
||||
inline void ObjectMonitor::release_clear_owner(void* old_value) {
|
||||
DEBUG_ONLY(void* prev = Atomic::load(&_owner);)
|
||||
assert(prev == old_value, "unexpected prev owner=" INTPTR_FORMAT
|
||||
", expected=" INTPTR_FORMAT, p2i(prev), p2i(old_value));
|
||||
void* prev = Atomic::load(&_owner);
|
||||
ADIM_guarantee(prev == old_value, "unexpected prev owner=" INTPTR_FORMAT
|
||||
", expected=" INTPTR_FORMAT, p2i(prev), p2i(old_value));
|
||||
Atomic::release_store(&_owner, (void*)NULL);
|
||||
log_trace(monitorinflation, owner)("release_clear_owner(): mid="
|
||||
INTPTR_FORMAT ", old_value=" INTPTR_FORMAT,
|
||||
@ -99,9 +136,9 @@ inline void ObjectMonitor::release_clear_owner(void* old_value) {
|
||||
// Simply set _owner field to new_value; current value must match old_value.
|
||||
// (Simple means no memory sync needed.)
|
||||
inline void ObjectMonitor::set_owner_from(void* old_value, void* new_value) {
|
||||
DEBUG_ONLY(void* prev = Atomic::load(&_owner);)
|
||||
assert(prev == old_value, "unexpected prev owner=" INTPTR_FORMAT
|
||||
", expected=" INTPTR_FORMAT, p2i(prev), p2i(old_value));
|
||||
void* prev = Atomic::load(&_owner);
|
||||
ADIM_guarantee(prev == old_value, "unexpected prev owner=" INTPTR_FORMAT
|
||||
", expected=" INTPTR_FORMAT, p2i(prev), p2i(old_value));
|
||||
Atomic::store(&_owner, new_value);
|
||||
log_trace(monitorinflation, owner)("set_owner_from(): mid="
|
||||
INTPTR_FORMAT ", old_value=" INTPTR_FORMAT
|
||||
@ -109,11 +146,28 @@ inline void ObjectMonitor::set_owner_from(void* old_value, void* new_value) {
|
||||
p2i(old_value), p2i(new_value));
|
||||
}
|
||||
|
||||
// Simply set _owner field to new_value; current value must match old_value1 or old_value2.
|
||||
// (Simple means no memory sync needed.)
|
||||
inline void ObjectMonitor::set_owner_from(void* old_value1, void* old_value2, void* new_value) {
|
||||
void* prev = Atomic::load(&_owner);
|
||||
ADIM_guarantee(prev == old_value1 || prev == old_value2,
|
||||
"unexpected prev owner=" INTPTR_FORMAT ", expected1="
|
||||
INTPTR_FORMAT " or expected2=" INTPTR_FORMAT, p2i(prev),
|
||||
p2i(old_value1), p2i(old_value2));
|
||||
_owner = new_value;
|
||||
log_trace(monitorinflation, owner)("set_owner_from(old1=" INTPTR_FORMAT
|
||||
", old2=" INTPTR_FORMAT "): mid="
|
||||
INTPTR_FORMAT ", prev=" INTPTR_FORMAT
|
||||
", new=" INTPTR_FORMAT, p2i(old_value1),
|
||||
p2i(old_value2), p2i(this), p2i(prev),
|
||||
p2i(new_value));
|
||||
}
|
||||
|
||||
// Simply set _owner field to self; current value must match basic_lock_p.
|
||||
inline void ObjectMonitor::set_owner_from_BasicLock(void* basic_lock_p, Thread* self) {
|
||||
DEBUG_ONLY(void* prev = Atomic::load(&_owner);)
|
||||
assert(prev == basic_lock_p, "unexpected prev owner=" INTPTR_FORMAT
|
||||
", expected=" INTPTR_FORMAT, p2i(prev), p2i(basic_lock_p));
|
||||
void* prev = Atomic::load(&_owner);
|
||||
ADIM_guarantee(prev == basic_lock_p, "unexpected prev owner=" INTPTR_FORMAT
|
||||
", expected=" INTPTR_FORMAT, p2i(prev), p2i(basic_lock_p));
|
||||
// Non-null owner field to non-null owner field is safe without
|
||||
// cmpxchg() as long as all readers can tolerate either flavor.
|
||||
Atomic::store(&_owner, self);
|
||||
@ -137,6 +191,26 @@ inline void* ObjectMonitor::try_set_owner_from(void* old_value, void* new_value)
|
||||
return prev;
|
||||
}
|
||||
|
||||
inline void ObjectMonitor::set_allocation_state(ObjectMonitor::AllocationState s) {
|
||||
_allocation_state = s;
|
||||
}
|
||||
|
||||
inline ObjectMonitor::AllocationState ObjectMonitor::allocation_state() const {
|
||||
return _allocation_state;
|
||||
}
|
||||
|
||||
inline bool ObjectMonitor::is_free() const {
|
||||
return _allocation_state == Free;
|
||||
}
|
||||
|
||||
inline bool ObjectMonitor::is_old() const {
|
||||
return _allocation_state == Old;
|
||||
}
|
||||
|
||||
inline bool ObjectMonitor::is_new() const {
|
||||
return _allocation_state == New;
|
||||
}
|
||||
|
||||
// The _next_om field can be concurrently read and modified so we
|
||||
// use Atomic operations to disable compiler optimizations that
|
||||
// might try to elide loading and/or storing this field.
|
||||
|
@ -490,8 +490,9 @@ void SafepointSynchronize::end() {
|
||||
}
|
||||
|
||||
bool SafepointSynchronize::is_cleanup_needed() {
|
||||
// Need a safepoint if there are many monitors to deflate.
|
||||
if (ObjectSynchronizer::is_cleanup_needed()) return true;
|
||||
// Need a cleanup safepoint if there are too many monitors in use
|
||||
// and the monitor deflation needs to be done at a safepoint.
|
||||
if (ObjectSynchronizer::is_safepoint_deflation_needed()) return true;
|
||||
// Need a safepoint if some inline cache buffers is non-empty
|
||||
if (!InlineCacheBuffer::is_empty()) return true;
|
||||
if (StringTable::needs_rehashing()) return true;
|
||||
@ -510,6 +511,10 @@ public:
|
||||
_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()) {
|
||||
@ -542,7 +547,11 @@ public:
|
||||
const char* name = "deflating global idle monitors";
|
||||
EventSafepointCleanupTask event;
|
||||
TraceTime timer(name, TRACETIME_LOG(Info, safepoint, cleanup));
|
||||
ObjectSynchronizer::deflate_idle_monitors(_counters);
|
||||
// AsyncDeflateIdleMonitors only uses DeflateMonitorCounters
|
||||
// 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(_counters);
|
||||
|
||||
post_safepoint_cleanup_task_event(event, safepoint_id, name);
|
||||
}
|
||||
|
@ -110,6 +110,7 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) {
|
||||
bool thread_id_table_work = false;
|
||||
bool protection_domain_table_work = false;
|
||||
bool oopstorage_work = false;
|
||||
bool deflate_idle_monitors = false;
|
||||
JvmtiDeferredEvent jvmti_event;
|
||||
{
|
||||
// Need state transition ThreadBlockInVM so that this thread
|
||||
@ -136,10 +137,14 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) {
|
||||
(resolved_method_table_work = ResolvedMethodTable::has_work()) |
|
||||
(thread_id_table_work = ThreadIdTable::has_work()) |
|
||||
(protection_domain_table_work = SystemDictionary::pd_cache_table()->has_work()) |
|
||||
(oopstorage_work = OopStorage::has_cleanup_work_and_reset())
|
||||
(oopstorage_work = OopStorage::has_cleanup_work_and_reset()) |
|
||||
(deflate_idle_monitors = ObjectSynchronizer::is_async_deflation_needed())
|
||||
) == 0) {
|
||||
// Wait until notified that there is some work to do.
|
||||
ml.wait();
|
||||
// If AsyncDeflateIdleMonitors, then we wait for
|
||||
// GuaranteedSafepointInterval so that is_async_deflation_needed()
|
||||
// is checked at the same interval.
|
||||
ml.wait(AsyncDeflateIdleMonitors ? GuaranteedSafepointInterval : 0);
|
||||
}
|
||||
|
||||
if (has_jvmti_events) {
|
||||
@ -191,6 +196,10 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) {
|
||||
if (oopstorage_work) {
|
||||
cleanup_oopstorages();
|
||||
}
|
||||
|
||||
if (deflate_idle_monitors) {
|
||||
ObjectSynchronizer::deflate_idle_monitors_using_JT();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,6 +65,7 @@
|
||||
#include "runtime/javaCalls.hpp"
|
||||
#include "runtime/sharedRuntime.hpp"
|
||||
#include "runtime/stubRoutines.hpp"
|
||||
#include "runtime/synchronizer.hpp"
|
||||
#include "runtime/vframe.inline.hpp"
|
||||
#include "runtime/vframeArray.hpp"
|
||||
#include "utilities/copy.hpp"
|
||||
@ -3070,10 +3071,15 @@ JRT_LEAF(intptr_t*, SharedRuntime::OSR_migration_begin( JavaThread *thread) )
|
||||
kptr2 = fr.next_monitor_in_interpreter_frame(kptr2) ) {
|
||||
if (kptr2->obj() != NULL) { // Avoid 'holes' in the monitor array
|
||||
BasicLock *lock = kptr2->lock();
|
||||
// Inflate so the displaced header becomes position-independent
|
||||
if (lock->displaced_header().is_unlocked())
|
||||
// Inflate so the object's header no longer refers to the BasicLock.
|
||||
if (lock->displaced_header().is_unlocked()) {
|
||||
// The object is locked and the resulting ObjectMonitor* will also be
|
||||
// locked so it can't be async deflated until ownership is dropped.
|
||||
// See the big comment in basicLock.cpp: BasicLock::move_to().
|
||||
ObjectSynchronizer::inflate_helper(kptr2->obj());
|
||||
// Now the displaced header is free to move
|
||||
}
|
||||
// Now the displaced header is free to move because the
|
||||
// object's header no longer refers to it.
|
||||
buf[i++] = (intptr_t)lock->displaced_header().value();
|
||||
buf[i++] = cast_from_oop<intptr_t>(kptr2->obj());
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -43,11 +43,11 @@ class ThreadsList;
|
||||
typedef PaddedEnd<ObjectMonitor, OM_CACHE_LINE_SIZE> PaddedObjectMonitor;
|
||||
|
||||
struct DeflateMonitorCounters {
|
||||
int n_in_use; // currently associated with objects
|
||||
int n_in_circulation; // extant
|
||||
int n_scavenged; // reclaimed (global and per-thread)
|
||||
int per_thread_scavenged; // per-thread scavenge total
|
||||
double per_thread_times; // per-thread scavenge times
|
||||
volatile int n_in_use; // currently associated with objects
|
||||
volatile int n_in_circulation; // extant
|
||||
volatile int n_scavenged; // reclaimed (global and per-thread)
|
||||
volatile int per_thread_scavenged; // per-thread scavenge total
|
||||
double per_thread_times; // per-thread scavenge times
|
||||
};
|
||||
|
||||
class ObjectSynchronizer : AllStatic {
|
||||
@ -132,6 +132,10 @@ class ObjectSynchronizer : AllStatic {
|
||||
// Basically we deflate all monitors that are not busy.
|
||||
// An adaptive profile-based deflation policy could be used if needed
|
||||
static void deflate_idle_monitors(DeflateMonitorCounters* counters);
|
||||
static void deflate_idle_monitors_using_JT();
|
||||
static void deflate_global_idle_monitors_using_JT();
|
||||
static void deflate_per_thread_idle_monitors_using_JT(JavaThread* target);
|
||||
static void deflate_common_idle_monitors_using_JT(bool is_global, JavaThread* target);
|
||||
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);
|
||||
@ -141,10 +145,26 @@ class ObjectSynchronizer : AllStatic {
|
||||
int* count_p,
|
||||
ObjectMonitor** free_head_p,
|
||||
ObjectMonitor** free_tail_p);
|
||||
// For a given in-use monitor list: global or per-thread, deflate idle
|
||||
// monitors using a JavaThread.
|
||||
static int deflate_monitor_list_using_JT(ObjectMonitor** list_p,
|
||||
int* count_p,
|
||||
ObjectMonitor** free_head_p,
|
||||
ObjectMonitor** free_tail_p,
|
||||
ObjectMonitor** saved_mid_in_use_p);
|
||||
static bool deflate_monitor(ObjectMonitor* mid, oop obj,
|
||||
ObjectMonitor** free_head_p,
|
||||
ObjectMonitor** free_tail_p);
|
||||
static bool is_cleanup_needed();
|
||||
static bool deflate_monitor_using_JT(ObjectMonitor* mid,
|
||||
ObjectMonitor** free_head_p,
|
||||
ObjectMonitor** free_tail_p);
|
||||
static bool is_async_deflation_needed();
|
||||
static bool is_safepoint_deflation_needed();
|
||||
static bool is_async_deflation_requested() { return _is_async_deflation_requested; }
|
||||
static bool is_special_deflation_requested() { return _is_special_deflation_requested; }
|
||||
static void set_is_async_deflation_requested(bool new_value) { _is_async_deflation_requested = new_value; }
|
||||
static void set_is_special_deflation_requested(bool new_value) { _is_special_deflation_requested = new_value; }
|
||||
static jlong time_since_last_async_deflation_ms();
|
||||
static void oops_do(OopClosure* f);
|
||||
// Process oops in thread local used monitors
|
||||
static void thread_local_used_oops_do(Thread* thread, OopClosure* f);
|
||||
@ -155,6 +175,8 @@ class ObjectSynchronizer : AllStatic {
|
||||
outputStream * out, int *error_cnt_p);
|
||||
static void chk_global_free_list_and_count(outputStream * out,
|
||||
int *error_cnt_p);
|
||||
static void chk_global_wait_list_and_count(outputStream * out,
|
||||
int *error_cnt_p);
|
||||
static void chk_global_in_use_list_and_count(outputStream * out,
|
||||
int *error_cnt_p);
|
||||
static void chk_in_use_entry(JavaThread* jt, ObjectMonitor* n,
|
||||
@ -169,12 +191,17 @@ class ObjectSynchronizer : AllStatic {
|
||||
static int log_monitor_list_counts(outputStream * out);
|
||||
static int verify_objmon_isinpool(ObjectMonitor *addr) PRODUCT_RETURN0;
|
||||
|
||||
static void do_safepoint_work(DeflateMonitorCounters* counters);
|
||||
|
||||
private:
|
||||
friend class SynchronizerTest;
|
||||
|
||||
enum { _BLOCKSIZE = 128 };
|
||||
// global list of blocks of monitors
|
||||
static PaddedObjectMonitor* g_block_list;
|
||||
static volatile bool _is_async_deflation_requested;
|
||||
static volatile bool _is_special_deflation_requested;
|
||||
static jlong _last_async_deflation_time_ns;
|
||||
|
||||
// Function to prepend new blocks to the appropriate lists:
|
||||
static void prepend_block_to_lists(PaddedObjectMonitor* new_blk);
|
||||
|
@ -4692,6 +4692,8 @@ GrowableArray<JavaThread*>* Threads::get_pending_threads(ThreadsList * t_list,
|
||||
DO_JAVA_THREADS(t_list, p) {
|
||||
if (!p->can_call_java()) continue;
|
||||
|
||||
// The first stage of async deflation does not affect any field
|
||||
// used by this comparison so the ObjectMonitor* is usable here.
|
||||
address pending = (address)p->current_pending_monitor();
|
||||
if (pending == monitor) { // found a match
|
||||
if (i < count) result->append(p); // save the first count matches
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -122,8 +122,14 @@ GrowableArray<MonitorInfo*>* javaVFrame::locked_monitors() {
|
||||
if (mons->is_empty()) return result;
|
||||
|
||||
bool found_first_monitor = false;
|
||||
ObjectMonitor *pending_monitor = thread()->current_pending_monitor();
|
||||
// The ObjectMonitor* can't be async deflated since we are either
|
||||
// at a safepoint or the calling thread is operating on itself so
|
||||
// it cannot exit the ObjectMonitor so it remains busy.
|
||||
ObjectMonitor *waiting_monitor = thread()->current_waiting_monitor();
|
||||
ObjectMonitor *pending_monitor = NULL;
|
||||
if (waiting_monitor == NULL) {
|
||||
pending_monitor = thread()->current_pending_monitor();
|
||||
}
|
||||
oop pending_obj = (pending_monitor != NULL ? (oop) pending_monitor->object() : (oop) NULL);
|
||||
oop waiting_obj = (waiting_monitor != NULL ? (oop) waiting_monitor->object() : (oop) NULL);
|
||||
|
||||
@ -231,6 +237,8 @@ void javaVFrame::print_lock_info_on(outputStream* st, int frame_count) {
|
||||
// an inflated monitor that is first on the monitor list in
|
||||
// the first frame can block us on a monitor enter.
|
||||
markWord mark = monitor->owner()->mark();
|
||||
// The first stage of async deflation does not affect any field
|
||||
// used by this comparison so the ObjectMonitor* is usable here.
|
||||
if (mark.has_monitor() &&
|
||||
( // we have marked ourself as pending on this monitor
|
||||
mark.monitor() == thread()->current_pending_monitor() ||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -41,6 +41,7 @@
|
||||
#include "runtime/frame.inline.hpp"
|
||||
#include "runtime/interfaceSupport.inline.hpp"
|
||||
#include "runtime/sweeper.hpp"
|
||||
#include "runtime/synchronizer.hpp"
|
||||
#include "runtime/thread.inline.hpp"
|
||||
#include "runtime/threadSMR.inline.hpp"
|
||||
#include "runtime/vmOperations.hpp"
|
||||
@ -433,6 +434,17 @@ int VM_Exit::wait_for_threads_in_native_to_block() {
|
||||
}
|
||||
}
|
||||
|
||||
bool VM_Exit::doit_prologue() {
|
||||
if (AsyncDeflateIdleMonitors && log_is_enabled(Info, monitorinflation)) {
|
||||
// AsyncDeflateIdleMonitors does a special deflation at the VM_Exit
|
||||
// safepoint in order to reduce the in-use monitor population that
|
||||
// is reported by ObjectSynchronizer::log_in_use_monitor_details()
|
||||
// at VM exit.
|
||||
ObjectSynchronizer::set_is_special_deflation_requested(true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void VM_Exit::doit() {
|
||||
|
||||
if (VerifyBeforeExit) {
|
||||
|
@ -420,6 +420,7 @@ class VM_Exit: public VM_Operation {
|
||||
}
|
||||
}
|
||||
VMOp_Type type() const { return VMOp_Exit; }
|
||||
bool doit_prologue();
|
||||
void doit();
|
||||
};
|
||||
|
||||
|
@ -94,6 +94,7 @@
|
||||
#include "runtime/serviceThread.hpp"
|
||||
#include "runtime/sharedRuntime.hpp"
|
||||
#include "runtime/stubRoutines.hpp"
|
||||
#include "runtime/synchronizer.hpp"
|
||||
#include "runtime/thread.inline.hpp"
|
||||
#include "runtime/threadSMR.hpp"
|
||||
#include "runtime/vframeArray.hpp"
|
||||
@ -899,14 +900,14 @@ typedef HashtableEntry<InstanceKlass*, mtClass> KlassHashtableEntry;
|
||||
volatile_nonstatic_field(ObjectMonitor, _header, markWord) \
|
||||
unchecked_nonstatic_field(ObjectMonitor, _object, sizeof(void *)) /* NOTE: no type */ \
|
||||
unchecked_nonstatic_field(ObjectMonitor, _owner, sizeof(void *)) /* NOTE: no type */ \
|
||||
volatile_nonstatic_field(ObjectMonitor, _contentions, jint) \
|
||||
volatile_nonstatic_field(ObjectMonitor, _next_om, ObjectMonitor*) \
|
||||
volatile_nonstatic_field(BasicLock, _displaced_header, markWord) \
|
||||
nonstatic_field(ObjectMonitor, _contentions, jint) \
|
||||
volatile_nonstatic_field(ObjectMonitor, _waiters, jint) \
|
||||
volatile_nonstatic_field(ObjectMonitor, _recursions, intx) \
|
||||
nonstatic_field(ObjectMonitor, _next_om, ObjectMonitor*) \
|
||||
volatile_nonstatic_field(BasicLock, _displaced_header, markWord) \
|
||||
nonstatic_field(BasicObjectLock, _lock, BasicLock) \
|
||||
nonstatic_field(BasicObjectLock, _obj, oop) \
|
||||
static_ptr_volatile_field(ObjectSynchronizer, g_block_list, PaddedObjectMonitor*) \
|
||||
static_field(ObjectSynchronizer, g_block_list, PaddedObjectMonitor*) \
|
||||
\
|
||||
/*********************/ \
|
||||
/* Matcher (C2 only) */ \
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include "runtime/mutexLocker.hpp"
|
||||
#include "runtime/os.hpp"
|
||||
#include "runtime/safepoint.hpp"
|
||||
#include "runtime/synchronizer.hpp"
|
||||
#include "runtime/thread.inline.hpp"
|
||||
#include "runtime/vmThread.hpp"
|
||||
#include "runtime/vmOperations.hpp"
|
||||
@ -283,6 +284,14 @@ void VMThread::run() {
|
||||
assert(should_terminate(), "termination flag must be set");
|
||||
}
|
||||
|
||||
if (AsyncDeflateIdleMonitors && log_is_enabled(Info, monitorinflation)) {
|
||||
// AsyncDeflateIdleMonitors does a special deflation at the final
|
||||
// safepoint in order to reduce the in-use monitor population that
|
||||
// is reported by ObjectSynchronizer::log_in_use_monitor_details()
|
||||
// at VM exit.
|
||||
ObjectSynchronizer::set_is_special_deflation_requested(true);
|
||||
}
|
||||
|
||||
// 4526887 let VM thread exit at Safepoint
|
||||
_cur_vm_operation = &halt_op;
|
||||
SafepointSynchronize::begin();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -208,19 +208,27 @@ Handle ThreadService::get_current_contended_monitor(JavaThread* thread) {
|
||||
assert(thread != NULL, "should be non-NULL");
|
||||
debug_only(Thread::check_for_dangling_thread_pointer(thread);)
|
||||
|
||||
// This function can be called on a target JavaThread that is not
|
||||
// the caller and we are not at a safepoint. So it is possible for
|
||||
// the waiting or pending condition to be over/stale and for the
|
||||
// first stage of async deflation to clear the object field in
|
||||
// the ObjectMonitor. It is also possible for the object to be
|
||||
// inflated again and to be associated with a completely different
|
||||
// ObjectMonitor by the time this object reference is processed
|
||||
// by the caller.
|
||||
ObjectMonitor *wait_obj = thread->current_waiting_monitor();
|
||||
|
||||
oop obj = NULL;
|
||||
if (wait_obj != NULL) {
|
||||
// thread is doing an Object.wait() call
|
||||
obj = (oop) wait_obj->object();
|
||||
assert(obj != NULL, "Object.wait() should have an object");
|
||||
assert(AsyncDeflateIdleMonitors || obj != NULL, "Object.wait() should have an object");
|
||||
} else {
|
||||
ObjectMonitor *enter_obj = thread->current_pending_monitor();
|
||||
if (enter_obj != NULL) {
|
||||
// thread is trying to enter() an ObjectMonitor.
|
||||
obj = (oop) enter_obj->object();
|
||||
assert(obj != NULL, "ObjectMonitor should have an associated object!");
|
||||
assert(AsyncDeflateIdleMonitors || obj != NULL, "ObjectMonitor should have an associated object!");
|
||||
}
|
||||
}
|
||||
|
||||
@ -391,6 +399,7 @@ DeadlockCycle* ThreadService::find_deadlocks_at_safepoint(ThreadsList * t_list,
|
||||
|
||||
cycle->reset();
|
||||
|
||||
// The ObjectMonitor* can't be async deflated since we are at a safepoint.
|
||||
// When there is a deadlock, all the monitors involved in the dependency
|
||||
// cycle must be contended and heavyweight. So we only care about the
|
||||
// heavyweight monitor a thread is waiting to lock.
|
||||
@ -967,13 +976,13 @@ void DeadlockCycle::print_on_with(ThreadsList * t_list, outputStream* st) const
|
||||
st->print("=============================");
|
||||
|
||||
JavaThread* currentThread;
|
||||
ObjectMonitor* waitingToLockMonitor;
|
||||
JvmtiRawMonitor* waitingToLockRawMonitor;
|
||||
oop waitingToLockBlocker;
|
||||
int len = _threads->length();
|
||||
for (int i = 0; i < len; i++) {
|
||||
currentThread = _threads->at(i);
|
||||
waitingToLockMonitor = currentThread->current_pending_monitor();
|
||||
// The ObjectMonitor* can't be async deflated since we are at a safepoint.
|
||||
ObjectMonitor* waitingToLockMonitor = currentThread->current_pending_monitor();
|
||||
waitingToLockRawMonitor = currentThread->current_pending_raw_monitor();
|
||||
waitingToLockBlocker = currentThread->current_park_blocker();
|
||||
st->cr();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -121,6 +121,10 @@ TEST_VM(markWord, printing) {
|
||||
// This is no longer biased, because ObjectLocker revokes the bias.
|
||||
assert_test_pattern(h_obj, "is_neutral no_hash");
|
||||
|
||||
// Hash the object then print it.
|
||||
intx hash = h_obj->identity_hash();
|
||||
assert_test_pattern(h_obj, "is_neutral hash=0x");
|
||||
|
||||
// Wait gets the lock inflated.
|
||||
{
|
||||
ObjectLocker ol(h_obj, THREAD);
|
||||
@ -135,14 +139,18 @@ TEST_VM(markWord, printing) {
|
||||
done.wait_with_safepoint_check(THREAD); // wait till the thread is done.
|
||||
}
|
||||
|
||||
// Make the object older. Not all GCs use this field.
|
||||
Universe::heap()->collect(GCCause::_java_lang_system_gc);
|
||||
if (UseParallelGC) {
|
||||
assert_test_pattern(h_obj, "is_neutral no_hash age 1");
|
||||
}
|
||||
if (!AsyncDeflateIdleMonitors) {
|
||||
// With AsyncDeflateIdleMonitors, the collect() call below
|
||||
// does not guarantee monitor deflation.
|
||||
// Make the object older. Not all GCs use this field.
|
||||
Universe::heap()->collect(GCCause::_java_lang_system_gc);
|
||||
if (UseParallelGC) {
|
||||
assert_test_pattern(h_obj, "is_neutral no_hash age 1");
|
||||
}
|
||||
|
||||
// Hash the object then print it.
|
||||
intx hash = h_obj->identity_hash();
|
||||
assert_test_pattern(h_obj, "is_neutral hash=0x");
|
||||
// Hash the object then print it.
|
||||
intx hash = h_obj->identity_hash();
|
||||
assert_test_pattern(h_obj, "is_neutral hash=0x");
|
||||
}
|
||||
}
|
||||
#endif // PRODUCT
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -29,12 +29,17 @@
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* java.management
|
||||
* @run driver SafepointCleanupTest
|
||||
* @run driver SafepointCleanupTest -XX:+AsyncDeflateIdleMonitors
|
||||
*/
|
||||
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
public class SafepointCleanupTest {
|
||||
static final String ASYNC_DISABLE_OPTION = "-XX:-AsyncDeflateIdleMonitors";
|
||||
static final String ASYNC_ENABLE_OPTION = "-XX:+AsyncDeflateIdleMonitors";
|
||||
static final String UNLOCK_DIAG_OPTION = "-XX:+UnlockDiagnosticVMOptions";
|
||||
|
||||
static void analyzeOutputOn(ProcessBuilder pb) throws Exception {
|
||||
OutputAnalyzer output = new OutputAnalyzer(pb.start());
|
||||
output.shouldContain("[safepoint,cleanup]");
|
||||
@ -53,19 +58,40 @@ public class SafepointCleanupTest {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String async_option;
|
||||
if (args.length == 0) {
|
||||
// By default test deflating idle monitors at a safepoint.
|
||||
async_option = ASYNC_DISABLE_OPTION;
|
||||
} else {
|
||||
async_option = args[0];
|
||||
}
|
||||
if (!async_option.equals(ASYNC_DISABLE_OPTION) &&
|
||||
!async_option.equals(ASYNC_ENABLE_OPTION)) {
|
||||
throw new RuntimeException("Unknown async_option value: '"
|
||||
+ async_option + "'");
|
||||
}
|
||||
|
||||
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-Xlog:safepoint+cleanup=info",
|
||||
UNLOCK_DIAG_OPTION,
|
||||
async_option,
|
||||
InnerClass.class.getName());
|
||||
analyzeOutputOn(pb);
|
||||
|
||||
pb = ProcessTools.createJavaProcessBuilder("-XX:+TraceSafepointCleanupTime",
|
||||
UNLOCK_DIAG_OPTION,
|
||||
async_option,
|
||||
InnerClass.class.getName());
|
||||
analyzeOutputOn(pb);
|
||||
|
||||
pb = ProcessTools.createJavaProcessBuilder("-Xlog:safepoint+cleanup=off",
|
||||
UNLOCK_DIAG_OPTION,
|
||||
async_option,
|
||||
InnerClass.class.getName());
|
||||
analyzeOutputOff(pb);
|
||||
|
||||
pb = ProcessTools.createJavaProcessBuilder("-XX:-TraceSafepointCleanupTime",
|
||||
UNLOCK_DIAG_OPTION,
|
||||
async_option,
|
||||
InnerClass.class.getName());
|
||||
analyzeOutputOff(pb);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user