Skip to content

Commit be57cf1

Browse files
author
Daniel D. Daugherty
committed
8226416: MonitorUsedDeflationThreshold can cause repeated async deflation requests
Reviewed-by: dholmes, coleenp
1 parent c822eda commit be57cf1

File tree

3 files changed

+175
-7
lines changed

3 files changed

+175
-7
lines changed

src/hotspot/share/runtime/globals.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,11 @@ const intx ObjectAlignmentInBytes = 8;
733733
"or AsyncDeflationInterval.") \
734734
range(0, 100) \
735735
\
736+
product(uintx, NoAsyncDeflationProgressMax, 3, DIAGNOSTIC, \
737+
"Max number of no progress async deflation attempts to tolerate " \
738+
"before adjusting the in_use_list_ceiling up (0 is off).") \
739+
range(0, max_uintx) \
740+
\
736741
product(intx, hashCode, 5, EXPERIMENTAL, \
737742
"(Unstable) select hashCode generation algorithm") \
738743
\

src/hotspot/share/runtime/synchronizer.cpp

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -240,23 +240,31 @@ void ObjectSynchronizer::initialize() {
240240
}
241241

242242
static MonitorList _in_use_list;
243+
// monitors_used_above_threshold() policy is as follows:
244+
//
243245
// The ratio of the current _in_use_list count to the ceiling is used
244246
// to determine if we are above MonitorUsedDeflationThreshold and need
245247
// to do an async monitor deflation cycle. The ceiling is increased by
246248
// AvgMonitorsPerThreadEstimate when a thread is added to the system
247249
// and is decreased by AvgMonitorsPerThreadEstimate when a thread is
248250
// removed from the system.
251+
//
249252
// Note: If the _in_use_list max exceeds the ceiling, then
250253
// monitors_used_above_threshold() will use the in_use_list max instead
251254
// of the thread count derived ceiling because we have used more
252255
// ObjectMonitors than the estimated average.
253256
//
257+
// Note: If deflate_idle_monitors() has NoAsyncDeflationProgressMax
258+
// no-progress async monitor deflation cycles in a row, then the ceiling
259+
// is adjusted upwards by monitors_used_above_threshold().
260+
//
254261
// Start the ceiling with the estimate for one thread in initialize()
255262
// which is called after cmd line options are processed.
256263
static size_t _in_use_list_ceiling = 0;
257264
bool volatile ObjectSynchronizer::_is_async_deflation_requested = false;
258265
bool volatile ObjectSynchronizer::_is_final_audit = false;
259266
jlong ObjectSynchronizer::_last_async_deflation_time_ns = 0;
267+
static uintx _no_progress_cnt = 0;
260268

261269
// =====================> Quick functions
262270

@@ -1142,21 +1150,35 @@ void ObjectSynchronizer::monitors_iterate(MonitorClosure* closure) {
11421150
}
11431151

11441152
static bool monitors_used_above_threshold(MonitorList* list) {
1153+
if (MonitorUsedDeflationThreshold == 0) { // disabled case is easy
1154+
return false;
1155+
}
11451156
// Start with ceiling based on a per-thread estimate:
11461157
size_t ceiling = ObjectSynchronizer::in_use_list_ceiling();
1158+
size_t old_ceiling = ceiling;
11471159
if (ceiling < list->max()) {
11481160
// The max used by the system has exceeded the ceiling so use that:
11491161
ceiling = list->max();
11501162
}
1151-
if (ceiling == 0) {
1163+
size_t monitors_used = list->count();
1164+
if (monitors_used == 0) { // empty list is easy
11521165
return false;
11531166
}
1154-
if (MonitorUsedDeflationThreshold > 0) {
1155-
size_t monitors_used = list->count();
1156-
size_t monitor_usage = (monitors_used * 100LL) / ceiling;
1157-
return int(monitor_usage) > MonitorUsedDeflationThreshold;
1158-
}
1159-
return false;
1167+
if (NoAsyncDeflationProgressMax != 0 &&
1168+
_no_progress_cnt >= NoAsyncDeflationProgressMax) {
1169+
float remainder = (100.0 - MonitorUsedDeflationThreshold) / 100.0;
1170+
size_t new_ceiling = ceiling + (ceiling * remainder) + 1;
1171+
ObjectSynchronizer::set_in_use_list_ceiling(new_ceiling);
1172+
log_info(monitorinflation)("Too many deflations without progress; "
1173+
"bumping in_use_list_ceiling from " SIZE_FORMAT
1174+
" to " SIZE_FORMAT, old_ceiling, new_ceiling);
1175+
_no_progress_cnt = 0;
1176+
ceiling = new_ceiling;
1177+
}
1178+
1179+
// Check if our monitor usage is above the threshold:
1180+
size_t monitor_usage = (monitors_used * 100LL) / ceiling;
1181+
return int(monitor_usage) > MonitorUsedDeflationThreshold;
11601182
}
11611183

11621184
size_t ObjectSynchronizer::in_use_list_ceiling() {
@@ -1583,6 +1605,12 @@ size_t ObjectSynchronizer::deflate_idle_monitors() {
15831605

15841606
GVars.stw_random = os::random();
15851607

1608+
if (deflated_count != 0) {
1609+
_no_progress_cnt = 0;
1610+
} else {
1611+
_no_progress_cnt++;
1612+
}
1613+
15861614
return deflated_count;
15871615
}
15881616

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/*
2+
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
import jdk.test.lib.process.OutputAnalyzer;
25+
import jdk.test.lib.Platform;
26+
import jdk.test.lib.process.ProcessTools;
27+
28+
/*
29+
* @test
30+
* @bug 8226416
31+
* @summary Test the MonitorUsedDeflationThreshold and NoAsyncDeflationProgressMax options.
32+
* @modules java.base/jdk.internal.misc
33+
* @library /test/lib
34+
* @run driver MonitorUsedDeflationThresholdTest
35+
*/
36+
37+
public class MonitorUsedDeflationThresholdTest {
38+
public static final int DELAY_SECS = 10;
39+
public static int inflate_count = 0;
40+
public static Object[] monitors;
41+
42+
public static void do_work(int count) {
43+
System.out.println("Recursion count=" + count);
44+
if (count > inflate_count) {
45+
System.out.println("Exceeded inflate_count=" + inflate_count);
46+
47+
System.out.println("Delaying for " + DELAY_SECS + " secs.");
48+
try {
49+
Thread.sleep(DELAY_SECS * 1000);
50+
} catch (InterruptedException ie) {
51+
// ignore InterruptedException
52+
}
53+
System.out.println("Done delaying for " + DELAY_SECS + " secs.");
54+
return;
55+
}
56+
57+
synchronized(monitors[count]) {
58+
try {
59+
monitors[count].wait(1); // force inflation
60+
} catch (InterruptedException ie) {
61+
// ignore InterruptedException
62+
}
63+
do_work(count + 1);
64+
}
65+
}
66+
67+
public static void usage() {
68+
System.err.println("Usage: java " +
69+
"MonitorUsedDeflationThresholdTest inflate_count");
70+
}
71+
72+
public static void main(String[] args) throws Exception {
73+
if (args.length == 0) {
74+
// Without args we invoke the test in a java sub-process:
75+
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
76+
// Test doesn't need much Java heap:
77+
"-Xmx100M",
78+
// AvgMonitorsPerThreadEstimate == 1 means we'll start with
79+
// an in_use_list_ceiling of <n-threads> plus a couple of
80+
// of monitors for threads that call Object.wait().
81+
"-XX:+UnlockDiagnosticVMOptions",
82+
"-XX:AvgMonitorsPerThreadEstimate=1",
83+
// Enable monitorinflation logging so we can see that
84+
// MonitorUsedDeflationThreshold and
85+
// NoAsyncDeflationProgressMaxoption are working.
86+
"-Xlog:monitorinflation=info",
87+
// Enable some safepoint logging for diagnostic purposes.
88+
"-Xlog:safepoint+cleanup=info",
89+
"-Xlog:safepoint+stats=debug",
90+
// Run the test with inflate_count == 33 since that
91+
// reproduced the bug with JDK13. Anything above the
92+
// in_use_list_ceiling will do the trick.
93+
"MonitorUsedDeflationThresholdTest", "33");
94+
95+
OutputAnalyzer output_detail = new OutputAnalyzer(pb.start());
96+
97+
// This mesg means:
98+
// - AvgMonitorsPerThreadEstimate == 1 reduced in_use_list_ceiling
99+
// to a small number.
100+
// - and we crossed MonitorUsedDeflationThreshold:
101+
output_detail.shouldMatch("begin deflating: .*");
102+
System.out.println("Found beginning of a deflation cycle.");
103+
104+
// This mesg means we hit NoAsyncDeflationProgressMax and
105+
// had to adjust the in_use_list_ceiling:
106+
String too_many = output_detail.firstMatch("Too many deflations without progress; .*", 0);
107+
if (too_many == null) {
108+
output_detail.reportDiagnosticSummary();
109+
throw new RuntimeException("Did not find too_many string in output.\n");
110+
}
111+
System.out.println("too_many='" + too_many + "'");
112+
113+
System.out.println("PASSED.");
114+
return;
115+
}
116+
// else we are the exec'd java subprocess, so run the actual test:
117+
118+
try {
119+
inflate_count = Integer.decode(args[0]);
120+
} catch (NumberFormatException nfe) {
121+
usage();
122+
throw new RuntimeException("ERROR: '" + args[0] +
123+
"': bad inflate_count.");
124+
}
125+
126+
System.out.println("Hello from MonitorUsedDeflationThresholdTest!");
127+
System.out.println("inflate_count=" + inflate_count);
128+
129+
monitors = new Object[inflate_count + 1];
130+
for (int i = 1; i <= inflate_count; i++) {
131+
monitors[i] = new Object();
132+
}
133+
do_work(1);
134+
}
135+
}

0 commit comments

Comments
 (0)