2
* Copyright (c) 2023 SAP SE. All rights reserved.
3
* Copyright (c) 2023 Red Hat Inc. All rights reserved.
4
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
5
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
7
* This code is free software; you can redistribute it and/or modify it
8
* under the terms of the GNU General Public License version 2 only, as
9
* published by the Free Software Foundation.
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
27
#include "precompiled.hpp"
28
#include "logging/log.hpp"
29
#include "runtime/globals.hpp"
30
#include "runtime/globals_extension.hpp"
31
#include "runtime/mutex.hpp"
32
#include "runtime/mutexLocker.hpp"
33
#include "runtime/nonJavaThread.hpp"
34
#include "runtime/os.inline.hpp"
35
#include "runtime/safepoint.hpp"
36
#include "runtime/trimNativeHeap.hpp"
37
#include "utilities/debug.hpp"
38
#include "utilities/globalDefinitions.hpp"
39
#include "utilities/ostream.hpp"
40
#include "utilities/vmError.hpp"
42
class NativeHeapTrimmerThread : public NamedThread {
44
// Upper limit for the backoff during pending/in-progress safepoint.
45
// Chosen as reasonable value to balance the overheads of waking up
46
// during the safepoint, which might have undesired effects on latencies,
47
// and the accuracy in tracking the trimming interval.
48
static constexpr int64_t safepoint_poll_ms = 250;
52
uint16_t _suspend_count;
55
uint64_t _num_trims_performed;
57
bool is_suspended() const {
58
assert(_lock->is_locked(), "Must be");
59
return _suspend_count > 0;
62
uint16_t inc_suspend_count() {
63
assert(_lock->is_locked(), "Must be");
64
assert(_suspend_count < UINT16_MAX, "Sanity");
65
return ++_suspend_count;
68
uint16_t dec_suspend_count() {
69
assert(_lock->is_locked(), "Must be");
70
assert(_suspend_count != 0, "Sanity");
71
return --_suspend_count;
74
bool at_or_nearing_safepoint() const {
75
return SafepointSynchronize::is_at_safepoint() ||
76
SafepointSynchronize::is_synchronizing();
80
static double now() { return os::elapsedTime(); }
81
static double to_ms(double seconds) { return seconds * 1000.0; }
83
struct LogStartStopMark {
84
void log(const char* s) { log_info(trimnative)("Native heap trimmer %s", s); }
85
LogStartStopMark() { log("start"); }
86
~LogStartStopMark() { log("stop"); }
90
assert(NativeHeapTrimmer::enabled(), "Only call if enabled");
92
LogStartStopMark lssm;
94
const double interval_secs = (double)TrimNativeHeapInterval / 1000;
98
double next_trim_time = tnow + interval_secs;
100
unsigned times_suspended = 0;
101
unsigned times_waited = 0;
102
unsigned times_safepoint = 0;
105
MonitorLocker ml(_lock, Mutex::_no_safepoint_check_flag);
108
while (at_or_nearing_safepoint() || is_suspended() || next_trim_time > tnow) {
109
if (is_suspended()) {
111
ml.wait(0); // infinite
112
} else if (next_trim_time > tnow) {
114
const double wait_ms = MAX2(1.0, to_ms(next_trim_time - tnow));
115
ml.wait((int64_t)wait_ms);
116
} else if (at_or_nearing_safepoint()) {
118
const int64_t wait_ms = MIN2<int64_t>(TrimNativeHeapInterval, safepoint_poll_ms);
128
log_trace(trimnative)("Times: %u suspended, %u timed, %u safepoint",
129
times_suspended, times_waited, times_safepoint);
131
execute_trim_and_log(tnow);
135
// Execute the native trim, log results.
136
void execute_trim_and_log(double t1) {
137
assert(os::can_trim_native_heap(), "Unexpected");
139
os::size_change_t sc = { 0, 0 };
140
LogTarget(Info, trimnative) lt;
141
const bool logging_enabled = lt.is_enabled();
143
// We only collect size change information if we are logging; save the access to procfs otherwise.
144
if (os::trim_native_heap(logging_enabled ? &sc : nullptr)) {
145
_num_trims_performed++;
146
if (logging_enabled) {
148
if (sc.after != SIZE_MAX) {
149
const size_t delta = sc.after < sc.before ? (sc.before - sc.after) : (sc.after - sc.before);
150
const char sign = sc.after < sc.before ? '-' : '+';
151
log_info(trimnative)("Periodic Trim (" UINT64_FORMAT "): " PROPERFMT "->" PROPERFMT " (%c" PROPERFMT ") %.3fms",
152
_num_trims_performed,
153
PROPERFMTARGS(sc.before), PROPERFMTARGS(sc.after), sign, PROPERFMTARGS(delta),
156
log_info(trimnative)("Periodic Trim (" UINT64_FORMAT "): complete (no details) %.3fms",
157
_num_trims_performed,
166
NativeHeapTrimmerThread() :
167
_lock(new (std::nothrow) PaddedMonitor(Mutex::nosafepoint, "NativeHeapTrimmer_lock")),
170
_num_trims_performed(0)
172
set_name("Native Heap Trimmer");
173
if (os::create_thread(this, os::vm_thread)) {
174
os::start_thread(this);
178
void suspend(const char* reason) {
179
assert(NativeHeapTrimmer::enabled(), "Only call if enabled");
182
MonitorLocker ml(_lock, Mutex::_no_safepoint_check_flag);
183
n = inc_suspend_count();
184
// No need to wakeup trimmer
186
log_debug(trimnative)("Trim suspended for %s (%u suspend requests)", reason, n);
189
void resume(const char* reason) {
190
assert(NativeHeapTrimmer::enabled(), "Only call if enabled");
193
MonitorLocker ml(_lock, Mutex::_no_safepoint_check_flag);
194
n = dec_suspend_count();
196
ml.notify_all(); // pause end
200
log_debug(trimnative)("Trim resumed after %s", reason);
202
log_debug(trimnative)("Trim still suspended after %s (%u suspend requests)", reason, n);
207
MonitorLocker ml(_lock, Mutex::_no_safepoint_check_flag);
212
void print_state(outputStream* st) const {
213
int64_t num_trims = 0;
214
bool stopped = false;
215
uint16_t suspenders = 0;
217
// Don't pull lock during error reporting
218
ConditionalMutexLocker ml(_lock, !VMError::is_error_reported(), Mutex::_no_safepoint_check_flag);
219
num_trims = _num_trims_performed;
221
suspenders = _suspend_count;
223
st->print_cr("Trims performed: " UINT64_FORMAT ", current suspend count: %d, stopped: %d",
224
num_trims, suspenders, stopped);
227
}; // NativeHeapTrimmer
229
static NativeHeapTrimmerThread* g_trimmer_thread = nullptr;
231
void NativeHeapTrimmer::initialize() {
232
assert(g_trimmer_thread == nullptr, "Only once");
233
if (TrimNativeHeapInterval > 0) {
234
if (!os::can_trim_native_heap()) {
235
FLAG_SET_ERGO(TrimNativeHeapInterval, 0);
236
log_warning(trimnative)("Native heap trim is not supported on this platform");
239
g_trimmer_thread = new NativeHeapTrimmerThread();
240
log_info(trimnative)("Periodic native trim enabled (interval: %u ms)", TrimNativeHeapInterval);
244
void NativeHeapTrimmer::cleanup() {
245
if (g_trimmer_thread != nullptr) {
246
g_trimmer_thread->stop();
250
void NativeHeapTrimmer::suspend_periodic_trim(const char* reason) {
251
if (g_trimmer_thread != nullptr) {
252
g_trimmer_thread->suspend(reason);
256
void NativeHeapTrimmer::resume_periodic_trim(const char* reason) {
257
if (g_trimmer_thread != nullptr) {
258
g_trimmer_thread->resume(reason);
262
void NativeHeapTrimmer::print_state(outputStream* st) {
263
if (g_trimmer_thread != nullptr) {
264
st->print_cr("Periodic native trim enabled (interval: %u ms)", TrimNativeHeapInterval);
265
g_trimmer_thread->print_state(st);
267
st->print_cr("Periodic native trim disabled");