8305994: Guarantee eventual async monitor deflation
Reviewed-by: simonis, stuefe, dcubed
This commit is contained in:
parent
41d6be4d80
commit
6b81342c22
@ -713,6 +713,13 @@ const int ObjectAlignmentInBytes = 8;
|
||||
"MonitorUsedDeflationThreshold is exceeded (0 is off).") \
|
||||
range(0, max_jint) \
|
||||
\
|
||||
/* notice: the max range value here is max_jint, not max_intx */ \
|
||||
/* because of overflow issue */ \
|
||||
product(intx, GuaranteedAsyncDeflationInterval, 60000, DIAGNOSTIC, \
|
||||
"Async deflate idle monitors every so many milliseconds even " \
|
||||
"when MonitorUsedDeflationThreshold is NOT exceeded (0 is off).") \
|
||||
range(0, max_jint) \
|
||||
\
|
||||
product(size_t, AvgMonitorsPerThreadEstimate, 1024, DIAGNOSTIC, \
|
||||
"Used to estimate a variable ceiling based on number of threads " \
|
||||
"for use with MonitorUsedDeflationThreshold (0 is off).") \
|
||||
|
@ -47,6 +47,11 @@ void MonitorDeflationThread::initialize() {
|
||||
}
|
||||
|
||||
void MonitorDeflationThread::monitor_deflation_thread_entry(JavaThread* jt, TRAPS) {
|
||||
|
||||
// We wait for GuaranteedSafepointInterval so that is_async_deflation_needed() is checked
|
||||
// at the same interval, unless GuaranteedAsyncDeflationInterval is lower.
|
||||
const intx wait_time = MIN2(GuaranteedSafepointInterval, GuaranteedAsyncDeflationInterval);
|
||||
|
||||
while (true) {
|
||||
{
|
||||
// Need state transition ThreadBlockInVM so that this thread
|
||||
@ -58,9 +63,7 @@ void MonitorDeflationThread::monitor_deflation_thread_entry(JavaThread* jt, TRAP
|
||||
MonitorLocker ml(MonitorDeflation_lock, Mutex::_no_safepoint_check_flag);
|
||||
while (!ObjectSynchronizer::is_async_deflation_needed()) {
|
||||
// Wait until notified that there is some work to do.
|
||||
// We wait for GuaranteedSafepointInterval so that
|
||||
// is_async_deflation_needed() is checked at the same interval.
|
||||
ml.wait(GuaranteedSafepointInterval);
|
||||
ml.wait(wait_time);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -262,6 +262,9 @@ void ObjectSynchronizer::initialize() {
|
||||
}
|
||||
// Start the ceiling with the estimate for one thread.
|
||||
set_in_use_list_ceiling(AvgMonitorsPerThreadEstimate);
|
||||
|
||||
// Start the timer for deflations, so it does not trigger immediately.
|
||||
_last_async_deflation_time_ns = os::javaTimeNanos();
|
||||
}
|
||||
|
||||
MonitorList ObjectSynchronizer::_in_use_list;
|
||||
@ -290,6 +293,7 @@ bool volatile ObjectSynchronizer::_is_async_deflation_requested = false;
|
||||
bool volatile ObjectSynchronizer::_is_final_audit = false;
|
||||
jlong ObjectSynchronizer::_last_async_deflation_time_ns = 0;
|
||||
static uintx _no_progress_cnt = 0;
|
||||
static bool _no_progress_skip_increment = false;
|
||||
|
||||
// =====================> Quick functions
|
||||
|
||||
@ -1080,7 +1084,14 @@ static bool monitors_used_above_threshold(MonitorList* list) {
|
||||
|
||||
// Check if our monitor usage is above the threshold:
|
||||
size_t monitor_usage = (monitors_used * 100LL) / ceiling;
|
||||
return int(monitor_usage) > MonitorUsedDeflationThreshold;
|
||||
if (int(monitor_usage) > MonitorUsedDeflationThreshold) {
|
||||
log_info(monitorinflation)("monitors_used=" SIZE_FORMAT ", ceiling=" SIZE_FORMAT
|
||||
", monitor_usage=" SIZE_FORMAT ", threshold=" INTX_FORMAT,
|
||||
monitors_used, ceiling, monitor_usage, MonitorUsedDeflationThreshold);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t ObjectSynchronizer::in_use_list_ceiling() {
|
||||
@ -1102,17 +1113,49 @@ void ObjectSynchronizer::set_in_use_list_ceiling(size_t new_value) {
|
||||
bool ObjectSynchronizer::is_async_deflation_needed() {
|
||||
if (is_async_deflation_requested()) {
|
||||
// Async deflation request.
|
||||
log_info(monitorinflation)("Async deflation needed: explicit request");
|
||||
return true;
|
||||
}
|
||||
|
||||
jlong time_since_last = time_since_last_async_deflation_ms();
|
||||
|
||||
if (AsyncDeflationInterval > 0 &&
|
||||
time_since_last_async_deflation_ms() > AsyncDeflationInterval &&
|
||||
time_since_last > AsyncDeflationInterval &&
|
||||
monitors_used_above_threshold(&_in_use_list)) {
|
||||
// It's been longer than our specified deflate interval and there
|
||||
// are too many monitors in use. We don't deflate more frequently
|
||||
// than AsyncDeflationInterval (unless is_async_deflation_requested)
|
||||
// in order to not swamp the MonitorDeflationThread.
|
||||
log_info(monitorinflation)("Async deflation needed: monitors used are above the threshold");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (GuaranteedAsyncDeflationInterval > 0 &&
|
||||
time_since_last > GuaranteedAsyncDeflationInterval) {
|
||||
// It's been longer than our specified guaranteed deflate interval.
|
||||
// We need to clean up the used monitors even if the threshold is
|
||||
// not reached, to keep the memory utilization at bay when many threads
|
||||
// touched many monitors.
|
||||
log_info(monitorinflation)("Async deflation needed: guaranteed interval (" INTX_FORMAT " ms) "
|
||||
"is greater than time since last deflation (" JLONG_FORMAT " ms)",
|
||||
GuaranteedAsyncDeflationInterval, time_since_last);
|
||||
|
||||
// If this deflation has no progress, then it should not affect the no-progress
|
||||
// tracking, otherwise threshold heuristics would think it was triggered, experienced
|
||||
// no progress, and needs to backoff more aggressively. In this "no progress" case,
|
||||
// the generic code would bump the no-progress counter, and we compensate for that
|
||||
// by telling it to skip the update.
|
||||
//
|
||||
// If this deflation has progress, then it should let non-progress tracking
|
||||
// know about this, otherwise the threshold heuristics would kick in, potentially
|
||||
// experience no-progress due to aggressive cleanup by this deflation, and think
|
||||
// it is still in no-progress stride. In this "progress" case, the generic code would
|
||||
// zero the counter, and we allow it to happen.
|
||||
_no_progress_skip_increment = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1530,6 +1573,8 @@ size_t ObjectSynchronizer::deflate_idle_monitors(ObjectMonitorsHashtable* table)
|
||||
|
||||
if (deflated_count != 0) {
|
||||
_no_progress_cnt = 0;
|
||||
} else if (_no_progress_skip_increment) {
|
||||
_no_progress_skip_increment = false;
|
||||
} else {
|
||||
_no_progress_cnt++;
|
||||
}
|
||||
|
@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright Amazon.com Inc. 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
/*
|
||||
* @test id=allDisabled
|
||||
* @bug 8305994
|
||||
* @summary Test the GuaranteedAsyncDeflationInterval option
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @run driver GuaranteedAsyncDeflationIntervalTest allDisabled
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=guaranteedNoMUDT
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @run driver GuaranteedAsyncDeflationIntervalTest guaranteedNoMUDT
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=guaranteedNoADI
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @run driver GuaranteedAsyncDeflationIntervalTest guaranteedNoADI
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=allEnabled
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @run driver GuaranteedAsyncDeflationIntervalTest allEnabled
|
||||
*/
|
||||
|
||||
public class GuaranteedAsyncDeflationIntervalTest {
|
||||
|
||||
public static class Test {
|
||||
// Inflate a lot of monitors, so that threshold heuristics definitely fires
|
||||
public static final int MONITORS = 10_000;
|
||||
|
||||
public static Object[] monitors;
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
monitors = new Object[MONITORS];
|
||||
for (int i = 0; i < MONITORS; i++) {
|
||||
Object o = new Object();
|
||||
synchronized (o) {
|
||||
try {
|
||||
o.wait(1); // Inflate!
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
monitors[i] = o;
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(10_000);
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length < 1) {
|
||||
throw new IllegalArgumentException("Expect the test label");
|
||||
}
|
||||
|
||||
String test = args[0];
|
||||
switch (test) {
|
||||
case "allDisabled":
|
||||
testAllDisabled();
|
||||
break;
|
||||
case "guaranteedNoMUDT":
|
||||
testGuaranteedNoMUDT();
|
||||
break;
|
||||
case "guaranteedNoADI":
|
||||
testGuaranteedNoADI();
|
||||
break;
|
||||
case "allEnabled":
|
||||
testAllEnabled();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown test: " + test);
|
||||
}
|
||||
}
|
||||
|
||||
static final String MSG_THRESHOLD = "Async deflation needed: monitors used are above the threshold";
|
||||
static final String MSG_GUARANTEED = "Async deflation needed: guaranteed interval";
|
||||
|
||||
// Try with all heuristics disabled
|
||||
public static void testAllDisabled() throws Exception {
|
||||
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
|
||||
"-Xmx100M",
|
||||
"-XX:+UnlockDiagnosticVMOptions",
|
||||
"-XX:GuaranteedAsyncDeflationInterval=0",
|
||||
"-XX:AsyncDeflationInterval=0",
|
||||
"-XX:MonitorUsedDeflationThreshold=0",
|
||||
"-Xlog:monitorinflation=info",
|
||||
"GuaranteedAsyncDeflationIntervalTest$Test");
|
||||
|
||||
OutputAnalyzer oa = new OutputAnalyzer(pb.start());
|
||||
oa.shouldHaveExitValue(0);
|
||||
|
||||
oa.shouldNotContain(MSG_THRESHOLD);
|
||||
oa.shouldNotContain(MSG_GUARANTEED);
|
||||
assertNoDeflations(oa);
|
||||
}
|
||||
|
||||
// Try with guaranteed interval only enabled, threshold heuristics disabled via MUDT
|
||||
public static void testGuaranteedNoMUDT() throws Exception {
|
||||
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
|
||||
"-Xmx100M",
|
||||
"-XX:+UnlockDiagnosticVMOptions",
|
||||
"-XX:GuaranteedAsyncDeflationInterval=100",
|
||||
"-XX:MonitorUsedDeflationThreshold=0",
|
||||
"-Xlog:monitorinflation=info",
|
||||
"GuaranteedAsyncDeflationIntervalTest$Test");
|
||||
|
||||
OutputAnalyzer oa = new OutputAnalyzer(pb.start());
|
||||
oa.shouldHaveExitValue(0);
|
||||
|
||||
oa.shouldNotContain(MSG_THRESHOLD);
|
||||
oa.shouldContain(MSG_GUARANTEED);
|
||||
assertDeflations(oa);
|
||||
}
|
||||
|
||||
// Try with guaranteed interval only enabled, threshold heuristics disabled via ADI
|
||||
public static void testGuaranteedNoADI() throws Exception {
|
||||
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
|
||||
"-Xmx100M",
|
||||
"-XX:+UnlockDiagnosticVMOptions",
|
||||
"-XX:GuaranteedAsyncDeflationInterval=100",
|
||||
"-XX:AsyncDeflationInterval=0",
|
||||
"-Xlog:monitorinflation=info",
|
||||
"GuaranteedAsyncDeflationIntervalTest$Test");
|
||||
|
||||
OutputAnalyzer oa = new OutputAnalyzer(pb.start());
|
||||
oa.shouldHaveExitValue(0);
|
||||
|
||||
oa.shouldNotContain(MSG_THRESHOLD);
|
||||
oa.shouldContain(MSG_GUARANTEED);
|
||||
assertDeflations(oa);
|
||||
}
|
||||
|
||||
// Try with both threshold heuristics and guaranteed interval enabled
|
||||
public static void testAllEnabled() throws Exception {
|
||||
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
|
||||
"-Xmx100M",
|
||||
"-XX:+UnlockDiagnosticVMOptions",
|
||||
"-XX:GuaranteedAsyncDeflationInterval=5000",
|
||||
"-XX:MonitorUsedDeflationThreshold=10",
|
||||
"-Xlog:monitorinflation=info",
|
||||
"GuaranteedAsyncDeflationIntervalTest$Test");
|
||||
|
||||
OutputAnalyzer oa = new OutputAnalyzer(pb.start());
|
||||
oa.shouldHaveExitValue(0);
|
||||
|
||||
oa.shouldContain(MSG_THRESHOLD);
|
||||
oa.shouldContain(MSG_GUARANTEED);
|
||||
assertDeflations(oa);
|
||||
}
|
||||
|
||||
private static void assertNoDeflations(OutputAnalyzer oa) {
|
||||
for (String line : oa.asLines()) {
|
||||
if (line.contains("Starting the final audit")) {
|
||||
// Final deflations started, with no prior deflations, good.
|
||||
return;
|
||||
}
|
||||
if (line.contains("begin deflating")) {
|
||||
// Deflations detected before final ones, bad
|
||||
oa.reportDiagnosticSummary();
|
||||
throw new IllegalStateException("FAILED");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertDeflations(OutputAnalyzer oa) {
|
||||
for (String line : oa.asLines()) {
|
||||
if (line.contains("Starting the final audit")) {
|
||||
// Final deflations started, with no prior deflations, bad.
|
||||
oa.reportDiagnosticSummary();
|
||||
throw new IllegalStateException("FAILED");
|
||||
}
|
||||
if (line.contains("begin deflating")) {
|
||||
// Deflations detected before final ones, good
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user