openjdk-1.8.0/8293114-GC-should-trim-the-native-heap-and-bug-fix.patch
2023-01-28 14:19:32 +08:00

1504 lines
57 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From a5edc79220300bce7952feaacf28a832306884d8 Mon Sep 17 00:00:00 2001
From: eapen <zhangyipeng7@huawei.com>
Date: Mon, 12 Dec 2022 19:28:28 +0800
Subject: [PATCH 14/33] I68TO2: 8293114: GC should trim the native heap,8136854G1 ConcurrentG1RefineThread::stop delays JVM shutdown for >150ms
---
hotspot/src/os/aix/vm/os_aix.cpp | 5 +
hotspot/src/os/bsd/vm/os_bsd.cpp | 5 +
hotspot/src/os/linux/vm/os_linux.cpp | 149 ++++++-
hotspot/src/os/linux/vm/os_linux.hpp | 57 ++-
hotspot/src/os/linux/vm/trimCHeapDCmd.cpp | 59 +--
hotspot/src/os/windows/vm/os_windows.cpp | 4 +
.../vm/gc_implementation/g1/g1CollectedHeap.cpp | 6 +
.../parallelScavenge/parallelScavengeHeap.cpp | 3 +
.../parallelScavenge/psParallelCompact.cpp | 6 +
.../shared/concurrentGCThread.cpp | 8 +-
.../shared/concurrentGCThread.hpp | 6 +-
.../gc_implementation/shared/gcTrimNativeHeap.cpp | 246 ++++++++++++
.../gc_implementation/shared/gcTrimNativeHeap.hpp | 66 ++++
hotspot/src/share/vm/memory/genCollectedHeap.cpp | 6 +
hotspot/src/share/vm/memory/sharedHeap.cpp | 6 +
hotspot/src/share/vm/runtime/globals.hpp | 10 +
hotspot/src/share/vm/runtime/init.cpp | 5 +-
hotspot/src/share/vm/runtime/java.cpp | 3 +
hotspot/src/share/vm/runtime/os.hpp | 11 +
.../src/share/vm/utilities/globalDefinitions.hpp | 3 +
hotspot/test/gc/TestTrimNative.java | 435 +++++++++++++++++++++
.../test/serviceability/dcmd/TrimLibcHeapTest.java | 7 +-
22 files changed, 994 insertions(+), 112 deletions(-)
create mode 100644 hotspot/src/share/vm/gc_implementation/shared/gcTrimNativeHeap.cpp
create mode 100644 hotspot/src/share/vm/gc_implementation/shared/gcTrimNativeHeap.hpp
create mode 100644 hotspot/test/gc/TestTrimNative.java
diff --git a/hotspot/src/os/aix/vm/os_aix.cpp b/hotspot/src/os/aix/vm/os_aix.cpp
index b078bee..519b085 100644
--- a/hotspot/src/os/aix/vm/os_aix.cpp
+++ b/hotspot/src/os/aix/vm/os_aix.cpp
@@ -5266,3 +5266,8 @@ void TestReserveMemorySpecial_test() {
// No tests available for this platform
}
#endif
+
+// stubbed-out trim-native support
+bool os::can_trim_native_heap() { return false; }
+bool os::should_trim_native_heap() { return false; }
+bool os::trim_native_heap(os::size_change_t* rss_change) { return false; }
\ No newline at end of file
diff --git a/hotspot/src/os/bsd/vm/os_bsd.cpp b/hotspot/src/os/bsd/vm/os_bsd.cpp
index 340334c..85e2861 100644
--- a/hotspot/src/os/bsd/vm/os_bsd.cpp
+++ b/hotspot/src/os/bsd/vm/os_bsd.cpp
@@ -4899,3 +4899,8 @@ void TestReserveMemorySpecial_test() {
// No tests available for this platform
}
#endif
+
+// stubbed-out trim-native support
+bool os::can_trim_native_heap() { return false; }
+bool os::should_trim_native_heap() { return false; }
+bool os::trim_native_heap(os::size_change_t* rss_change) { return false; }
\ No newline at end of file
diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp
index 099dafa..abf2031 100644
--- a/hotspot/src/os/linux/vm/os_linux.cpp
+++ b/hotspot/src/os/linux/vm/os_linux.cpp
@@ -153,8 +153,28 @@ const char * os::Linux::_libpthread_version = NULL;
pthread_condattr_t os::Linux::_condattr[1];
#ifdef __GLIBC__
-os::Linux::mallinfo_func_t os::Linux::_mallinfo = NULL;
-os::Linux::mallinfo2_func_t os::Linux::_mallinfo2 = NULL;
+// We want to be runnable with both old and new glibcs.
+// Old glibcs offer mallinfo(). New glibcs deprecate mallinfo() and offer mallinfo2()
+// as replacement. Future glibc's may remove the deprecated mallinfo().
+// Therefore we may have one, both, or possibly neither (?). Code should tolerate all
+// cases, which is why we resolve the functions dynamically. Outside code should use
+// the Linux::get_mallinfo() utility function which exists to hide this mess.
+struct glibc_mallinfo {
+ int arena;
+ int ordblks;
+ int smblks;
+ int hblks;
+ int hblkhd;
+ int usmblks;
+ int fsmblks;
+ int uordblks;
+ int fordblks;
+ int keepcost;
+};
+typedef struct glibc_mallinfo (*mallinfo_func_t)(void);
+typedef struct os::Linux::glibc_mallinfo2 (*mallinfo2_func_t)(void);
+static mallinfo_func_t g_mallinfo = NULL;
+static mallinfo2_func_t g_mallinfo2 = NULL;
#endif // __GLIBC__
static jlong initial_time_count=0;
@@ -2348,23 +2368,21 @@ void os::Linux::print_process_memory_info(outputStream* st) {
// Print glibc outstanding allocations.
// (note: there is no implementation of mallinfo for muslc)
#ifdef __GLIBC__
- size_t total_allocated = 0;
bool might_have_wrapped = false;
- if (_mallinfo2 != NULL) {
- struct glibc_mallinfo2 mi = _mallinfo2();
- total_allocated = mi.uordblks;
- } else if (_mallinfo != NULL) {
- // mallinfo is an old API. Member names mean next to nothing and, beyond that, are int.
- // So values may have wrapped around. Still useful enough to see how much glibc thinks
- // we allocated.
- struct glibc_mallinfo mi = _mallinfo();
- total_allocated = (size_t)(unsigned)mi.uordblks;
- // Since mallinfo members are int, glibc values may have wrapped. Warn about this.
- might_have_wrapped = (info.vmrss * K) > UINT_MAX && (info.vmrss * K) > (total_allocated + UINT_MAX);
- }
- if (_mallinfo2 != NULL || _mallinfo != NULL) {
- st->print_cr("C-Heap outstanding allocations: " SIZE_FORMAT "K%s",
- total_allocated / K,
+ glibc_mallinfo2 mi;
+ mallinfo_retval_t mirc = os::Linux::get_mallinfo(&mi);
+ if (mirc != os::Linux::error) {
+ size_t total_allocated = mi.uordblks + mi.hblkhd;
+ size_t free_retained = mi.fordblks;
+#ifdef _LP64
+ // If all we had is old mallinf(3), the values may have wrapped. Since that can confuse readers
+ // of this output, print a hint.
+ // We do this by checking virtual size of the process: if that is <4g, we could not have wrapped.
+ might_have_wrapped = (mirc == os::Linux::ok_but_possibly_wrapped) &&
+ ((info.vmsize * K) > UINT_MAX);
+#endif
+ st->print_cr("C-Heap outstanding allocations: " SIZE_FORMAT "K, retained: " SIZE_FORMAT "K%s",
+ total_allocated / K, free_retained / K,
might_have_wrapped ? " (may have wrapped)" : "");
}
@@ -5187,8 +5205,8 @@ void os::init(void) {
Linux::initialize_system_info();
#ifdef __GLIBC__
- Linux::_mallinfo = CAST_TO_FN_PTR(Linux::mallinfo_func_t, dlsym(RTLD_DEFAULT, "mallinfo"));
- Linux::_mallinfo2 = CAST_TO_FN_PTR(Linux::mallinfo2_func_t, dlsym(RTLD_DEFAULT, "mallinfo2"));
+ g_mallinfo = CAST_TO_FN_PTR(mallinfo_func_t, dlsym(RTLD_DEFAULT, "mallinfo"));
+ g_mallinfo2 = CAST_TO_FN_PTR(mallinfo2_func_t, dlsym(RTLD_DEFAULT, "mallinfo2"));
#endif // __GLIBC__
// _main_thread points to the thread that created/loaded the JVM.
@@ -6820,3 +6838,94 @@ void TestReserveMemorySpecial_test() {
}
#endif
+
+#ifdef __GLIBC__
+os::Linux::mallinfo_retval_t os::Linux::get_mallinfo(glibc_mallinfo2* out) {
+ if (g_mallinfo2) {
+ glibc_mallinfo2 mi = g_mallinfo2();
+ *out = mi;
+ return os::Linux::ok;
+ } else if (g_mallinfo) {
+ // mallinfo() returns 32-bit values. Not perfect but still useful if
+ // process virt size < 4g
+ glibc_mallinfo mi = g_mallinfo();
+ out->arena = (int) mi.arena;
+ out->ordblks = (int) mi.ordblks;
+ out->smblks = (int) mi.smblks;
+ out->hblks = (int) mi.hblks;
+ out->hblkhd = (int) mi.hblkhd;
+ out->usmblks = (int) mi.usmblks;
+ out->fsmblks = (int) mi.fsmblks;
+ out->uordblks = (int) mi.uordblks;
+ out->fordblks = (int) mi.fordblks;
+ out->keepcost = (int) mi.keepcost;
+ return os::Linux::ok_but_possibly_wrapped;
+ }
+ return os::Linux::ok;
+}
+#endif // __GLIBC__
+
+// Trim-native support
+bool os::can_trim_native_heap() {
+#ifdef __GLIBC__
+ return true;
+#else
+ return false; // musl
+#endif
+}
+
+static const size_t retain_size = 2 * M;
+
+bool os::should_trim_native_heap() {
+#ifdef __GLIBC__
+ bool rc = true;
+ // We try, using mallinfo, to predict whether a malloc_trim(3) will be beneficial.
+ //
+ // "mallinfo::keepcost" is no help even if manpage claims this to be the projected
+ // trim size. In practice it is just a very small value with no relation to the actual
+ // effect trimming will have.
+ //
+ // Our best bet is "mallinfo::fordblks", the total chunk size of free blocks. Since
+ // only free blocks can be trimmed, a very low bar is to require their combined size
+ // to be higher than our retain size. Note, however, that "mallinfo::fordblks" includes
+ // already-trimmed blocks, since glibc trims by calling madvice(MADV_DONT_NEED) on free
+ // chunks but does not update its bookkeeping.
+ //
+ // In the end we want to prevent obvious bogus attempts to trim, and for that fordblks
+ // is good enough.
+ os::Linux::glibc_mallinfo2 mi;
+ os::Linux::mallinfo_retval_t mirc = os::Linux::get_mallinfo(&mi);
+ const size_t total_free = mi.fordblks;
+ if (mirc == os::Linux::ok) {
+ rc = retain_size < total_free;
+ }
+ return rc;
+#else
+ return false; // musl
+#endif
+}
+
+bool os::trim_native_heap(os::size_change_t* rss_change) {
+#ifdef __GLIBC__
+ os::Linux::meminfo_t info1;
+ os::Linux::meminfo_t info2;
+
+ bool have_info1 = os::Linux::query_process_memory_info(&info1);
+ ::malloc_trim(retain_size);
+ bool have_info2 = have_info1 && os::Linux::query_process_memory_info(&info2);
+
+ if (have_info1 && have_info2 &&
+ info1.vmrss != -1 && info2.vmrss != -1 &&
+ info1.vmswap != -1 && info2.vmswap != -1) {
+ // Note: query_process_memory_info returns values in K
+ rss_change->before = (info1.vmrss + info1.vmswap) * K;
+ rss_change->after = (info2.vmrss + info2.vmswap) * K;
+ } else {
+ rss_change->after = rss_change->before = SIZE_MAX;
+ }
+
+ return true;
+#else
+ return false; // musl
+#endif
+}
\ No newline at end of file
diff --git a/hotspot/src/os/linux/vm/os_linux.hpp b/hotspot/src/os/linux/vm/os_linux.hpp
index 2bb3fd2..6c27bcb 100644
--- a/hotspot/src/os/linux/vm/os_linux.hpp
+++ b/hotspot/src/os/linux/vm/os_linux.hpp
@@ -243,7 +243,7 @@ class Linux {
public:
static pthread_condattr_t* condAttr() { return _condattr; }
- // Output structure for query_process_memory_info()
+ // Output structure for query_process_memory_info() (all values in KB)
struct meminfo_t {
ssize_t vmsize; // current virtual size
ssize_t vmpeak; // peak virtual size
@@ -338,40 +338,6 @@ private:
};
static NumaAllocationPolicy _current_numa_policy;
-#ifdef __GLIBC__
- struct glibc_mallinfo {
- int arena;
- int ordblks;
- int smblks;
- int hblks;
- int hblkhd;
- int usmblks;
- int fsmblks;
- int uordblks;
- int fordblks;
- int keepcost;
- };
-
- struct glibc_mallinfo2 {
- size_t arena;
- size_t ordblks;
- size_t smblks;
- size_t hblks;
- size_t hblkhd;
- size_t usmblks;
- size_t fsmblks;
- size_t uordblks;
- size_t fordblks;
- size_t keepcost;
- };
-
- typedef struct glibc_mallinfo (*mallinfo_func_t)(void);
- typedef struct glibc_mallinfo2 (*mallinfo2_func_t)(void);
-
- static mallinfo_func_t _mallinfo;
- static mallinfo2_func_t _mallinfo2;
-#endif
-
public:
static int sched_getcpu() { return _sched_getcpu != NULL ? _sched_getcpu() : -1; }
static int numa_node_to_cpus(int node, unsigned long *buffer, int bufferlen) {
@@ -484,6 +450,27 @@ public:
return false;
}
}
+
+#ifdef __GLIBC__
+ struct glibc_mallinfo2 {
+ size_t arena;
+ size_t ordblks;
+ size_t smblks;
+ size_t hblks;
+ size_t hblkhd;
+ size_t usmblks;
+ size_t fsmblks;
+ size_t uordblks;
+ size_t fordblks;
+ size_t keepcost;
+ };
+ enum mallinfo_retval_t { ok, error, ok_but_possibly_wrapped };
+ // get_mallinfo() is a wrapper for mallinfo/mallinfo2. It will prefer mallinfo2() if found.
+ // If we only have mallinfo(), values may be 32-bit truncated, which is signaled via
+ // "ok_but_possibly_wrapped".
+ static mallinfo_retval_t get_mallinfo(glibc_mallinfo2* out);
+#endif
+
};
diff --git a/hotspot/src/os/linux/vm/trimCHeapDCmd.cpp b/hotspot/src/os/linux/vm/trimCHeapDCmd.cpp
index 95d03d9..39d47a3 100644
--- a/hotspot/src/os/linux/vm/trimCHeapDCmd.cpp
+++ b/hotspot/src/os/linux/vm/trimCHeapDCmd.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 SAP SE. All rights reserved.
+ * Copyright (c) 2022 SAP SE. All rights reserved.
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -25,53 +25,28 @@
#include "precompiled.hpp"
#include "runtime/os.hpp"
+#include "trimCHeapDCmd.hpp"
#include "utilities/debug.hpp"
#include "utilities/ostream.hpp"
-#include "trimCHeapDCmd.hpp"
+#include "utilities/globalDefinitions.hpp"
#include <malloc.h>
void TrimCLibcHeapDCmd::execute(DCmdSource source, TRAPS) {
-#ifdef __GLIBC__
- stringStream ss_report(1024); // Note: before calling trim
-
- os::Linux::meminfo_t info1;
- os::Linux::meminfo_t info2;
- // Query memory before...
- bool have_info1 = os::Linux::query_process_memory_info(&info1);
-
- _output->print_cr("Attempting trim...");
- ::malloc_trim(0);
- _output->print_cr("Done.");
-
- // ...and after trim.
- bool have_info2 = os::Linux::query_process_memory_info(&info2);
-
- // Print report both to output stream as well to UL
- bool wrote_something = false;
- if (have_info1 && have_info2) {
- if (info1.vmsize != -1 && info2.vmsize != -1) {
- ss_report.print_cr("Virtual size before: " SSIZE_FORMAT "k, after: " SSIZE_FORMAT "k, (" SSIZE_FORMAT "k)",
- info1.vmsize, info2.vmsize, (info2.vmsize - info1.vmsize));
- wrote_something = true;
- }
- if (info1.vmrss != -1 && info2.vmrss != -1) {
- ss_report.print_cr("RSS before: " SSIZE_FORMAT "k, after: " SSIZE_FORMAT "k, (" SSIZE_FORMAT "k)",
- info1.vmrss, info2.vmrss, (info2.vmrss - info1.vmrss));
- wrote_something = true;
- }
- if (info1.vmswap != -1 && info2.vmswap != -1) {
- ss_report.print_cr("Swap before: " SSIZE_FORMAT "k, after: " SSIZE_FORMAT "k, (" SSIZE_FORMAT "k)",
- info1.vmswap, info2.vmswap, (info2.vmswap - info1.vmswap));
- wrote_something = true;
+ if (os::can_trim_native_heap()) {
+ os::size_change_t sc;
+ if (os::trim_native_heap(&sc)) {
+ _output->print("Trim native heap: ");
+ if (sc.after != SIZE_MAX) {
+ const size_t delta = sc.after < sc.before ? (sc.before - sc.after) : (sc.after - sc.before);
+ const char sign = sc.after < sc.before ? '-' : '+';
+ _output->print_cr("RSS+Swap: " PROPERFMT "->" PROPERFMT " (%c" PROPERFMT ")",
+ PROPERFMTARGS(sc.before), PROPERFMTARGS(sc.after), sign, PROPERFMTARGS(delta));
+ } else {
+ _output->print_cr("(no details available).");
+ }
}
+ } else {
+ _output->print_cr("Not available.");
}
- if (!wrote_something) {
- ss_report.print_raw("No details available.");
- }
-
- _output->print_raw(ss_report.base());
-#else
- _output->print_cr("Not available.");
-#endif
}
diff --git a/hotspot/src/os/windows/vm/os_windows.cpp b/hotspot/src/os/windows/vm/os_windows.cpp
index 500a412..25122de 100644
--- a/hotspot/src/os/windows/vm/os_windows.cpp
+++ b/hotspot/src/os/windows/vm/os_windows.cpp
@@ -5957,3 +5957,7 @@ void TestReserveMemorySpecial_test() {
}
#endif // PRODUCT
+// stubbed-out trim-native support
+bool os::can_trim_native_heap() { return false; }
+bool os::should_trim_native_heap() { return false; }
+bool os::trim_native_heap(os::size_change_t* rss_change) { return false; }
\ No newline at end of file
diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp
index ba156a2..7188925 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp
@@ -53,6 +53,7 @@
#include "gc_implementation/shared/gcTimer.hpp"
#include "gc_implementation/shared/gcTrace.hpp"
#include "gc_implementation/shared/gcTraceTime.hpp"
+#include "gc_implementation/shared/gcTrimNativeHeap.hpp"
#include "gc_implementation/shared/isGCActiveMark.hpp"
#include "memory/allocation.hpp"
#include "memory/heapInspection.hpp"
@@ -1304,6 +1305,9 @@ bool G1CollectedHeap::do_collection(bool explicit_gc,
TraceCollectorStats tcs(g1mm()->full_collection_counters());
TraceMemoryManagerStats tms(true /* fullGC */, gc_cause());
+ // Pause native trimming for the duration of the GC
+ GCTrimNative::pause_periodic_trim();
+
double start = os::elapsedTime();
g1_policy()->record_full_collection_start();
@@ -1546,6 +1550,8 @@ bool G1CollectedHeap::do_collection(bool explicit_gc,
gc_timer->register_gc_end();
gc_tracer->report_gc_end(gc_timer->gc_end(), gc_timer->time_partitions());
+
+ GCTrimNative::schedule_trim();
}
return true;
}
diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp
index 74c1584..1c47125 100644
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp
@@ -36,6 +36,7 @@
#include "gc_implementation/parallelScavenge/psScavenge.hpp"
#include "gc_implementation/parallelScavenge/vmPSOperations.hpp"
#include "gc_implementation/shared/gcHeapSummary.hpp"
+#include "gc_implementation/shared/gcTrimNativeHeap.hpp"
#include "gc_implementation/shared/gcWhen.hpp"
#include "memory/gcLocker.inline.hpp"
#include "oops/oop.inline.hpp"
@@ -147,6 +148,8 @@ void ParallelScavengeHeap::post_initialize() {
PSMarkSweep::initialize();
}
PSPromotionManager::initialize();
+
+ GCTrimNative::initialize(true);
}
void ParallelScavengeHeap::update_counters() {
diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp
index 3f103ee..26d64a1 100644
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp
@@ -42,6 +42,7 @@
#include "gc_implementation/shared/gcTimer.hpp"
#include "gc_implementation/shared/gcTrace.hpp"
#include "gc_implementation/shared/gcTraceTime.hpp"
+#include "gc_implementation/shared/gcTrimNativeHeap.hpp"
#include "gc_implementation/shared/isGCActiveMark.hpp"
#include "gc_interface/gcCause.hpp"
#include "memory/gcLocker.inline.hpp"
@@ -2008,6 +2009,9 @@ bool PSParallelCompact::invoke_no_policy(bool maximum_heap_compaction) {
return false;
}
+ // Pause native trimming for the duration of the GC
+ GCTrimNative::pause_periodic_trim();
+
ParallelScavengeHeap* heap = gc_heap();
_gc_timer.register_gc_start();
@@ -2182,6 +2186,8 @@ bool PSParallelCompact::invoke_no_policy(bool maximum_heap_compaction) {
// Resize the metaspace capactiy after a collection
MetaspaceGC::compute_new_size();
+ GCTrimNative::schedule_trim();
+
if (TraceGen1Time) accumulated_time()->stop();
if (PrintGC) {
diff --git a/hotspot/src/share/vm/gc_implementation/shared/concurrentGCThread.cpp b/hotspot/src/share/vm/gc_implementation/shared/concurrentGCThread.cpp
index e39fd7a..024499a 100644
--- a/hotspot/src/share/vm/gc_implementation/shared/concurrentGCThread.cpp
+++ b/hotspot/src/share/vm/gc_implementation/shared/concurrentGCThread.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2016, 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
@@ -40,12 +40,12 @@ ConcurrentGCThread::ConcurrentGCThread() :
_should_terminate(false), _has_terminated(false) {
};
-void ConcurrentGCThread::create_and_start() {
+void ConcurrentGCThread::create_and_start(ThreadPriority prio) {
if (os::create_thread(this, os::cgc_thread)) {
// XXX: need to set this to low priority
// unless "agressive mode" set; priority
// should be just less than that of VMThread.
- os::set_priority(this, NearMaxPriority);
+ os::set_priority(this, prio);
if (!_should_terminate && !DisableStartThread) {
os::start_thread(this);
}
@@ -63,7 +63,7 @@ void ConcurrentGCThread::initialize_in_thread() {
void ConcurrentGCThread::wait_for_universe_init() {
MutexLockerEx x(CGC_lock, Mutex::_no_safepoint_check_flag);
while (!is_init_completed() && !_should_terminate) {
- CGC_lock->wait(Mutex::_no_safepoint_check_flag, 200);
+ CGC_lock->wait(Mutex::_no_safepoint_check_flag, 1);
}
}
diff --git a/hotspot/src/share/vm/gc_implementation/shared/concurrentGCThread.hpp b/hotspot/src/share/vm/gc_implementation/shared/concurrentGCThread.hpp
index e87228b..1e16bf7 100644
--- a/hotspot/src/share/vm/gc_implementation/shared/concurrentGCThread.hpp
+++ b/hotspot/src/share/vm/gc_implementation/shared/concurrentGCThread.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2016, 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
@@ -33,7 +33,7 @@ class ConcurrentGCThread: public NamedThread {
friend class VMStructs;
protected:
- bool _should_terminate;
+ bool volatile _should_terminate;
bool _has_terminated;
enum CGC_flag_type {
@@ -50,7 +50,7 @@ protected:
static int reset_CGC_flag(int b) { return _CGC_flag &= ~b; }
// Create and start the thread (setting it's priority high.)
- void create_and_start();
+ void create_and_start(ThreadPriority prio = NearMaxPriority);
// Do initialization steps in the thread: record stack base and size,
// init thread local storage, set JNI handle block.
diff --git a/hotspot/src/share/vm/gc_implementation/shared/gcTrimNativeHeap.cpp b/hotspot/src/share/vm/gc_implementation/shared/gcTrimNativeHeap.cpp
new file mode 100644
index 0000000..b9bac56
--- /dev/null
+++ b/hotspot/src/share/vm/gc_implementation/shared/gcTrimNativeHeap.cpp
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2022 SAP SE. All rights reserved.
+ * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, Huawei Technologies Co., Ltd. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "gc_implementation/shared/concurrentGCThread.hpp"
+#include "gc_implementation/shared/gcTrimNativeHeap.hpp"
+#include "gc_implementation/g1/g1_globals.hpp"
+#include "runtime/globals.hpp"
+#include "runtime/globals_extension.hpp"
+#include "runtime/mutex.hpp"
+#include "runtime/mutexLocker.hpp"
+#include "runtime/os.hpp"
+#include "utilities/debug.hpp"
+#include "utilities/globalDefinitions.hpp"
+#include "utilities/ostream.hpp"
+#include "utilities/ticks.hpp"
+
+bool GCTrimNative::_async_mode = false;
+double GCTrimNative::_next_trim_not_before = 0;
+
+// GCTrimNative works in two modes:
+//
+// - async mode, where GCTrimNative runs a trimmer thread on behalf of the GC.
+// The trimmer thread will be doing all the trims, both periodically and
+// triggered from outside via GCTrimNative::schedule_trim().
+//
+// - synchronous mode, where the GC does the trimming itself in its own thread,
+// via GCTrimNative::should_trim() and GCTrimNative::execute_trim().
+//
+// The mode is set as argument to GCTrimNative::initialize().
+
+class NativeTrimmer : public ConcurrentGCThread {
+
+ Monitor* _lock;
+ volatile jlong _paused;
+ static NativeTrimmer* _the_trimmer;
+
+public:
+
+ virtual void run() {
+ initialize_in_thread();
+ wait_for_universe_init();
+
+ assert(GCTrimNativeHeap, "Sanity");
+ assert(os::can_trim_native_heap(), "Sanity");
+
+ gclog_or_tty->print_cr("NativeTrimmer started.");
+
+ // Note: GCTrimNativeHeapInterval=0 -> zero wait time -> indefinite waits, disabling periodic trim
+ const int64_t delay_ms = GCTrimNativeHeapInterval * 1000;
+ for (;;) {
+ MonitorLockerEx ml(_lock, Mutex::_no_safepoint_check_flag);
+ ml.wait(Mutex::_no_safepoint_check_flag, delay_ms);
+ if (_should_terminate) {
+ gclog_or_tty->print_cr("NativeTrimmer stopped.");
+ break;
+ }
+ jlong paused = Atomic::load(&_paused);
+ if (!paused && os::should_trim_native_heap()) {
+ GCTrimNative::do_trim();
+ }
+ }
+
+ terminate();
+ }
+
+ void stop() {
+ {
+ MutexLockerEx ml(Terminator_lock);
+ _should_terminate = true;
+ }
+
+ wakeup();
+
+ {
+ MutexLockerEx ml(Terminator_lock);
+ while (!_has_terminated) {
+ Terminator_lock->wait();
+ }
+ }
+ }
+
+protected:
+
+ void wakeup() {
+ MonitorLockerEx ml(_lock, Mutex::_no_safepoint_check_flag);
+ ml.notify_all();
+ }
+
+ void pause() {
+ Atomic::store(1, &_paused);
+ debug_only(gclog_or_tty->print_cr("NativeTrimmer paused"));
+ }
+
+ void unpause() {
+ Atomic::store(0, &_paused);
+ debug_only(gclog_or_tty->print_cr("NativeTrimmer unpaused"));
+ }
+
+public:
+
+ NativeTrimmer() :
+ _paused(0)
+ {
+ //Mutex::leaf+8 just for NativeTrimmer_lock
+ _lock = new (std::nothrow) Monitor(Mutex::leaf+8, "NativeTrimmer_lock", true);
+ set_name("NativeTrimmer Thread");
+ }
+
+ static bool is_enabled() {
+ return _the_trimmer != NULL;
+ }
+
+ static void start_trimmer() {
+ _the_trimmer = new NativeTrimmer();
+ _the_trimmer->create_and_start(NormPriority);
+ }
+
+ static void stop_trimmer() {
+ _the_trimmer->stop();
+ }
+
+ static void pause_periodic_trim() {
+ _the_trimmer->pause();
+ }
+
+ static void unpause_periodic_trim() {
+ _the_trimmer->unpause();
+ }
+
+ static void schedule_trim_now() {
+ _the_trimmer->unpause();
+ _the_trimmer->wakeup();
+ }
+
+}; // NativeTrimmer
+
+NativeTrimmer* NativeTrimmer::_the_trimmer = NULL;
+
+void GCTrimNative::do_trim() {
+ Ticks start = Ticks::now();
+ os::size_change_t sc;
+ if (os::trim_native_heap(&sc)) {
+ Tickspan trim_time = (Ticks::now() - start);
+ if (sc.after != SIZE_MAX) {
+ const size_t delta = sc.after < sc.before ? (sc.before - sc.after) : (sc.after - sc.before);
+ const char sign = sc.after < sc.before ? '-' : '+';
+ gclog_or_tty->print_cr("Trim native heap: RSS+Swap: " PROPERFMT "->" PROPERFMT " (%c" PROPERFMT "), %1.3fms",
+ PROPERFMTARGS(sc.before), PROPERFMTARGS(sc.after), sign, PROPERFMTARGS(delta),
+ trim_time.seconds() * 1000);
+ } else {
+ gclog_or_tty->print_cr("Trim native heap (no details)");
+ }
+ }
+}
+
+/// GCTrimNative outside facing methods
+
+void GCTrimNative::initialize(bool async_mode) {
+
+ if (GCTrimNativeHeap) {
+
+ if (!os::can_trim_native_heap()) {
+ FLAG_SET_ERGO(bool, GCTrimNativeHeap, false);
+ gclog_or_tty->print_cr("GCTrimNativeHeap disabled - trim-native not supported on this platform.");
+ return;
+ }
+
+ debug_only(gclog_or_tty->print_cr("GCTrimNativeHeap enabled."));
+
+ _async_mode = async_mode;
+
+ // If we are to run the trimmer on behalf of the GC:
+ if (_async_mode) {
+ NativeTrimmer::start_trimmer();
+ }
+
+ _next_trim_not_before = GCTrimNativeHeapInterval;
+ }
+}
+
+void GCTrimNative::cleanup() {
+ if (GCTrimNativeHeap) {
+ if (_async_mode) {
+ NativeTrimmer::stop_trimmer();
+ }
+ }
+}
+
+bool GCTrimNative::should_trim(bool ignore_delay) {
+ return
+ GCTrimNativeHeap && os::can_trim_native_heap() &&
+ (ignore_delay || (GCTrimNativeHeapInterval > 0 && os::elapsedTime() > _next_trim_not_before)) &&
+ os::should_trim_native_heap();
+}
+
+void GCTrimNative::execute_trim() {
+ if (GCTrimNativeHeap) {
+ assert(!_async_mode, "Only call for non-async mode");
+ do_trim();
+ _next_trim_not_before = os::elapsedTime() + GCTrimNativeHeapInterval;
+ }
+}
+
+void GCTrimNative::pause_periodic_trim() {
+ if (GCTrimNativeHeap) {
+ assert(_async_mode, "Only call for async mode");
+ NativeTrimmer::pause_periodic_trim();
+ }
+}
+
+void GCTrimNative::unpause_periodic_trim() {
+ if (GCTrimNativeHeap) {
+ assert(_async_mode, "Only call for async mode");
+ NativeTrimmer::unpause_periodic_trim();
+ }
+}
+
+void GCTrimNative::schedule_trim() {
+ if (GCTrimNativeHeap) {
+ assert(_async_mode, "Only call for async mode");
+ NativeTrimmer::schedule_trim_now();
+ }
+}
diff --git a/hotspot/src/share/vm/gc_implementation/shared/gcTrimNativeHeap.hpp b/hotspot/src/share/vm/gc_implementation/shared/gcTrimNativeHeap.hpp
new file mode 100644
index 0000000..f586093
--- /dev/null
+++ b/hotspot/src/share/vm/gc_implementation/shared/gcTrimNativeHeap.hpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2022 SAP SE. All rights reserved.
+ * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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.
+ *
+ */
+
+#ifndef SHARE_GC_SHARED_GCTRIMNATIVEHEAP_HPP
+#define SHARE_GC_SHARED_GCTRIMNATIVEHEAP_HPP
+
+#include "memory/allocation.hpp"
+
+class NativeTrimmer;
+
+class GCTrimNative : public AllStatic {
+ friend class NativeTrimmer;
+
+ static bool _async_mode;
+ static double _next_trim_not_before;
+
+ static void do_trim();
+
+public:
+
+ static void initialize(bool async_mode);
+ static void cleanup();
+
+ // Returns true if:
+ // - trimming is enabled and possible
+ // - trimming may have an actual effect (guess)
+ // - delay timer has expired (unless ignore_delay is true)
+ static bool should_trim(bool ignore_delay);
+
+ // Execute trim-native in this thread
+ static void execute_trim();
+
+ // Pause/unpause periodic trim
+ static void pause_periodic_trim();
+ static void unpause_periodic_trim();
+
+ // Schedule an explicit trim now; if periodic trims had been
+ // paused, they are unpaused.
+ static void schedule_trim();
+
+};
+
+#endif // SHARE_GC_SHARED_GCTRIMNATIVEHEAP_HPP
diff --git a/hotspot/src/share/vm/memory/genCollectedHeap.cpp b/hotspot/src/share/vm/memory/genCollectedHeap.cpp
index 20fbbfd..7df3d68 100644
--- a/hotspot/src/share/vm/memory/genCollectedHeap.cpp
+++ b/hotspot/src/share/vm/memory/genCollectedHeap.cpp
@@ -58,6 +58,7 @@
#if INCLUDE_ALL_GCS
#include "gc_implementation/concurrentMarkSweep/concurrentMarkSweepThread.hpp"
#include "gc_implementation/concurrentMarkSweep/vmCMSOperations.hpp"
+#include "gc_implementation/shared/gcTrimNativeHeap.hpp"
#endif // INCLUDE_ALL_GCS
#if INCLUDE_JFR
#include "jfr/jfr.hpp"
@@ -572,6 +573,11 @@ void GenCollectedHeap::do_collection(bool full,
update_full_collections_completed();
}
+ // Trim the native heap, without a delay since this is a full gc
+ if (full && GCTrimNative::should_trim(true)) {
+ GCTrimNative::execute_trim();
+ }
+
// Track memory usage and detect low memory after GC finishes
MemoryService::track_memory_usage();
diff --git a/hotspot/src/share/vm/memory/sharedHeap.cpp b/hotspot/src/share/vm/memory/sharedHeap.cpp
index ef22f01..8c02320 100644
--- a/hotspot/src/share/vm/memory/sharedHeap.cpp
+++ b/hotspot/src/share/vm/memory/sharedHeap.cpp
@@ -26,6 +26,7 @@
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionary.hpp"
#include "code/codeCache.hpp"
+#include "gc_implementation/shared/gcTrimNativeHeap.hpp"
#include "gc_interface/collectedHeap.inline.hpp"
#include "memory/sharedHeap.hpp"
#include "oops/oop.inline.hpp"
@@ -104,6 +105,11 @@ void SharedHeap::set_barrier_set(BarrierSet* bs) {
void SharedHeap::post_initialize() {
CollectedHeap::post_initialize();
ref_processing_init();
+ if (!UseSerialGC) {
+ GCTrimNative::initialize(true);
+ } else {
+ GCTrimNative::initialize(false); // false since we will call trim inside the collecting thread
+ }
}
void SharedHeap::ref_processing_init() {}
diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp
index 0dab18e..ec48c48 100644
--- a/hotspot/src/share/vm/runtime/globals.hpp
+++ b/hotspot/src/share/vm/runtime/globals.hpp
@@ -3387,6 +3387,16 @@ class CommandLineFlags {
"Number of entries we will try to leave on the stack " \
"during parallel gc") \
\
+ experimental(bool, GCTrimNativeHeap, false, \
+ "GC will attempt to trim the native heap periodically and at " \
+ "full GCs.") \
+ \
+ experimental(uintx, GCTrimNativeHeapInterval, 60, \
+ "If GCTrimNativeHeap is enabled: interval time, in seconds, in " \
+ "which the VM will attempt to trim the native heap. A value of " \
+ "0 disables periodic trimming while leaving trimming at full gc " \
+ "enabled.") \
+ \
/* stack parameters */ \
product_pd(intx, StackYellowPages, \
"Number of yellow zone (recoverable overflows) pages") \
diff --git a/hotspot/src/share/vm/runtime/init.cpp b/hotspot/src/share/vm/runtime/init.cpp
index d15e40d..d2e0f22 100644
--- a/hotspot/src/share/vm/runtime/init.cpp
+++ b/hotspot/src/share/vm/runtime/init.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2016, 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
@@ -166,8 +166,7 @@ void exit_globals() {
}
}
-
-static bool _init_completed = false;
+static volatile bool _init_completed = false;
bool is_init_completed() {
return _init_completed;
diff --git a/hotspot/src/share/vm/runtime/java.cpp b/hotspot/src/share/vm/runtime/java.cpp
index 4f290c8..54b980d 100644
--- a/hotspot/src/share/vm/runtime/java.cpp
+++ b/hotspot/src/share/vm/runtime/java.cpp
@@ -30,6 +30,7 @@
#include "code/codeCache.hpp"
#include "compiler/compileBroker.hpp"
#include "compiler/compilerOracle.hpp"
+#include "gc_implementation/shared/gcTrimNativeHeap.hpp"
#include "interpreter/bytecodeHistogram.hpp"
#include "jfr/jfrEvents.hpp"
#include "jfr/support/jfrThreadId.hpp"
@@ -509,6 +510,8 @@ void before_exit(JavaThread * thread) {
StatSampler::disengage();
StatSampler::destroy();
+ GCTrimNative::cleanup();
+
// Stop concurrent GC threads
Universe::heap()->stop();
diff --git a/hotspot/src/share/vm/runtime/os.hpp b/hotspot/src/share/vm/runtime/os.hpp
index 7ae49fd..acc57f4 100644
--- a/hotspot/src/share/vm/runtime/os.hpp
+++ b/hotspot/src/share/vm/runtime/os.hpp
@@ -333,6 +333,17 @@ class os: AllStatic {
static bool uncommit_memory(char* addr, size_t bytes);
static bool release_memory(char* addr, size_t bytes);
+ // Does the platform support trimming the native heap?
+ static bool can_trim_native_heap();
+
+ // Does the platform recommend trimming?
+ static bool should_trim_native_heap();
+
+ // Trim the C-heap. Returns RSS size change and optionally return the rss size change.
+ // If trim was done but size change could not be obtained, SIZE_MAX is returned for after size.
+ struct size_change_t { size_t before; size_t after; };
+ static bool trim_native_heap(size_change_t* rss_change);
+
// Touch memory pages that cover the memory range from start to end (exclusive)
// to make the OS back the memory range with actual memory.
// Current implementation may not touch the last page if unaligned addresses
diff --git a/hotspot/src/share/vm/utilities/globalDefinitions.hpp b/hotspot/src/share/vm/utilities/globalDefinitions.hpp
index 25f6f02..12eea20 100644
--- a/hotspot/src/share/vm/utilities/globalDefinitions.hpp
+++ b/hotspot/src/share/vm/utilities/globalDefinitions.hpp
@@ -260,6 +260,9 @@ inline T byte_size_in_proper_unit(T s) {
}
}
+#define PROPERFMT SIZE_FORMAT "%s"
+#define PROPERFMTARGS(S) byte_size_in_proper_unit(S), proper_unit_for_byte_size(S)
+
//----------------------------------------------------------------------------------------------------
// VM type definitions
diff --git a/hotspot/test/gc/TestTrimNative.java b/hotspot/test/gc/TestTrimNative.java
new file mode 100644
index 0000000..58d5405
--- /dev/null
+++ b/hotspot/test/gc/TestTrimNative.java
@@ -0,0 +1,435 @@
+/*
+ * Copyright (c) 2022 SAP SE. All rights reserved.
+ * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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.
+ *
+ */
+
+package gc;
+
+/*
+ * All these tests test the trim-native feature for all GCs.
+ * Trim-native is the ability to trim the C-heap as part of the GC cycle.
+ * This feature is controlled by -XX:+GCTrimNativeHeap (by default off).
+ * Trimming happens on full gc for all gcs. Shenandoah and G1 also support
+ * concurrent trimming (Shenandoah supports this without any ties to java
+ * heap occupancy).
+ *
+ */
+
+//// full gc tests /////
+
+/*
+ * @test id=fullgc-serial
+ * @summary Test that GCTrimNativeHeap works with Serial
+ * @requires vm.gc=="Serial"
+ * @requires os.family=="linux"
+ * @modules java.base/jdk.internal.misc
+ * @library /testlibrary
+ * @run driver gc.TestTrimNative test-fullgc serial
+ */
+
+/*
+ * @test id=fullgc-parallel
+ * @summary Test that GCTrimNativeHeap works with Parallel
+ * @requires vm.gc=="Parallel"
+ * @requires os.family=="linux"
+ * @modules java.base/jdk.internal.misc
+ * @library /testlibrary
+ * @run driver gc.TestTrimNative test-fullgc parallel
+ */
+
+/*
+ * @test id=fullgc-g1
+ * @summary Test that GCTrimNativeHeap works with G1
+ * @requires vm.gc=="G1"
+ * @requires os.family=="linux"
+ * @modules java.base/jdk.internal.misc
+ * @library /testlibrary
+ * @run driver gc.TestTrimNative test-fullgc g1
+ */
+
+//// auto mode tests /////
+
+// Note: not serial, since it does not do periodic trimming, only trimming on full gc
+
+/*
+ * @test id=auto-parallel
+ * @summary Test that GCTrimNativeHeap works with Parallel
+ * @requires vm.gc=="Parallel"
+ * @requires os.family=="linux"
+ * @modules java.base/jdk.internal.misc
+ * @library /testlibrary
+ * @run driver gc.TestTrimNative test-auto parallel
+ */
+
+/*
+ * @test id=auto-g1
+ * @summary Test that GCTrimNativeHeap works with G1
+ * @requires vm.gc=="G1"
+ * @requires os.family=="linux"
+ * @modules java.base/jdk.internal.misc
+ * @library /testlibrary
+ * @run driver gc.TestTrimNative test-auto g1
+ */
+
+
+//// test-auto-high-interval interval test /////
+
+// Note: not serial, since it does not do periodic trimming, only trimming on full gc
+
+/*
+ * @test id=auto-high-interval-parallel
+ * @summary Test that a high GCTrimNativeHeapInterval effectively disables automatic trimming
+ * @requires vm.gc=="Parallel"
+ * @requires os.family=="linux"
+ * @modules java.base/jdk.internal.misc
+ * @library /testlibrary
+ * @run driver gc.TestTrimNative test-auto-high-interval parallel
+ */
+
+/*
+ * @test id=auto-high-interval-g1
+ * @summary Test that a high GCTrimNativeHeapInterval effectively disables automatic trimming
+ * @requires vm.gc=="G1"
+ * @requires os.family=="linux"
+ * @modules java.base/jdk.internal.misc
+ * @library /testlibrary
+ * @run driver gc.TestTrimNative test-auto-high-interval g1
+ */
+
+//// test-auto-interval-0 test /////
+
+// Note: not serial, since it does not do periodic trimming, only trimming on full gc
+
+/*
+ * @test id=auto-zero-interval-parallel
+ * @summary Test that a GCTrimNativeHeapInterval=0 disables periodic trimming
+ * @requires vm.gc=="Parallel"
+ * @requires os.family=="linux"
+ * @modules java.base/jdk.internal.misc
+ * @library /testlibrary
+ * @run driver gc.TestTrimNative test-auto-zero-interval parallel
+ */
+
+/*
+ * @test id=auto-zero-interval-g1
+ * @summary Test that a GCTrimNativeHeapInterval=0 disables periodic trimming
+ * @requires vm.gc=="G1"
+ * @requires os.family=="linux"
+ * @modules java.base/jdk.internal.misc
+ * @library /testlibrary
+ * @run driver gc.TestTrimNative test-auto-zero-interval g1
+ */
+
+// Other tests
+
+/*
+ * @test id=off-explicit
+ * @summary Test that -GCTrimNative disables the feature
+ * @requires os.family=="linux"
+ * @modules java.base/jdk.internal.misc
+ * @library /testlibrary
+ * @run driver gc.TestTrimNative test-off-explicit
+ */
+
+/*
+ * @test id=off-by-default
+ * @summary Test that GCTrimNative is off by default
+ * @requires os.family=="linux"
+ * @modules java.base/jdk.internal.misc
+ * @library /testlibrary
+ * @run driver gc.TestTrimNative test-off-by-default
+ */
+
+/*
+ * @test id=off-on-other-platforms
+ * @summary Test that GCTrimNative is off on unsupportive platforms
+ * @requires os.family!="linux"
+ * @modules java.base/jdk.internal.misc
+ * @library /testlibrary
+ * @run driver gc.TestTrimNative test-off-on-other-platforms
+ */
+
+import sun.misc.Unsafe;
+import com.oracle.java.testlibrary.*;
+
+import java.lang.reflect.Field;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class TestTrimNative {
+
+ // Actual RSS increase is a lot larger than 4 MB. Depends on glibc overhead, and NMT malloc headers in debug VMs.
+ // We need small-grained allocations to make sure they actually increase RSS (all touched) and to see the
+ // glibc-retaining-memory effect.
+ static final int szAllocations = 16;
+ static final int totalAllocationsSize = 16 * 1024 * 1024; // 16 MB total
+ static final int numAllocations = totalAllocationsSize / szAllocations;
+
+ static long[] ptrs = new long[numAllocations];
+
+ enum Unit {
+ B(1), K(1024), M(1024*1024), G(1024*1024*1024);
+ public final long size;
+ Unit(long size) { this.size = size; }
+ }
+
+ enum GC {
+ serial, parallel, g1, shenandoah, z;
+ String getSwitchName() {
+ String s = name();
+ return "-XX:+Use" + s.substring(0, 1).toUpperCase() + s.substring(1) + "GC";
+ }
+ boolean isZ() { return this == GC.z; }
+ boolean isSerial() { return this == GC.serial; }
+ boolean isParallel() { return this == GC.parallel; }
+ boolean isG1() { return this == GC.g1; }
+ boolean isShenandoah() { return this == GC.shenandoah; }
+ }
+
+ static private boolean usesNativeTrimmer(GC gc) {
+ return gc.isG1() || gc.isParallel() || gc.isZ();
+ }
+
+ static private final OutputAnalyzer runTestWithOptions(String[] extraOptions, String[] testArgs) throws Exception {
+
+ List<String> allOptions = new ArrayList<String>();
+ allOptions.add("-XX:+UnlockExperimentalVMOptions");
+ allOptions.addAll(Arrays.asList(extraOptions));
+ allOptions.add("-Xmx128m");
+ allOptions.add("-Xms128m"); // Stabilize RSS
+ allOptions.add("-XX:+AlwaysPreTouch"); // Stabilize RSS
+
+ allOptions.add(TestTrimNative.class.getName());
+ allOptions.add("RUN");
+ allOptions.addAll(Arrays.asList(testArgs));
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(allOptions.toArray(new String[0]));
+ OutputAnalyzer output = new OutputAnalyzer(pb.start());
+ output.shouldHaveExitValue(0);
+ return output;
+
+ }
+
+ /**
+ * Given JVM output, look for a log line that describes a successful negative trim in the megabyte range
+ * like this:
+ * "[2.053s][debug][gc,trim] Trim native heap (retain size: 5120K): RSS+Swap: 271M->223M (-49112K), 2.834ms"
+ * (Note: we use the "properXXX" print routines, therefore units can differ)
+ * Check that the sum of all trim log lines comes to a total RSS reduction in the MB range
+ * @param output
+ * @param minExpected min number of trim lines expected in UL log
+ * @param maxExpected max number of trim lines expected in UL log
+ */
+ private final static void parseOutputAndLookForNegativeTrim(OutputAnalyzer output, int minExpected, int maxExpected) {
+ output.reportDiagnosticSummary();
+ List<String> lines = output.asLines();
+ Pattern pat = Pattern.compile(".*\\[gc,trim\\] Trim native heap.*RSS\\+Swap: (\\d+)([KMB])->(\\d+)([KMB]).*");
+ int numTrimsFound = 0;
+ long rssReductionTotal = 0;
+ for (String line : lines) {
+ Matcher mat = pat.matcher(line);
+ if (mat.matches()) {
+ long rss1 = Long.parseLong(mat.group(1)) * Unit.valueOf(mat.group(2)).size;
+ long rss2 = Long.parseLong(mat.group(3)) * Unit.valueOf(mat.group(4)).size;
+ System.out.println("Parsed Trim Line. rss1: " + rss1 + " rss2: " + rss2);
+ if (rss1 > rss2) {
+ rssReductionTotal += (rss1 - rss2);
+ }
+ numTrimsFound ++;
+ }
+ if (numTrimsFound > maxExpected) {
+ throw new RuntimeException("Abnormal high number of trim attempts found (more than " + maxExpected +
+ "). Does the interval setting not work?");
+ }
+ }
+ if (numTrimsFound < minExpected) {
+ throw new RuntimeException("We found fewer trim lines in UL log than expected (expected " + minExpected +
+ ", found " + numTrimsFound + ".");
+ }
+ // This is very fuzzy. We malloced X, free'd X, trimmed, measured the combined effect of all reductions.
+ // This does not take into effect mallocs or frees that may happen concurrently. But we expect to see *some*
+ // reduction somewhere. Test with a fudge factor.
+ float fudge = 0.8f;
+ long expectedMinimalReduction = (long) (totalAllocationsSize * fudge);
+ if (rssReductionTotal < expectedMinimalReduction) {
+ throw new RuntimeException("We did not see the expected RSS reduction in the UL log. Expected (with fudge)" +
+ " to see at least a combined reduction of " + expectedMinimalReduction + ".");
+ }
+ }
+
+ // Test that GCTrimNativeHeap=1 causes a trim-native on full gc
+ static private final void testWithFullGC(GC gc) throws Exception {
+ System.out.println("testWithFullGC");
+ int sleeptime_secs = 2;
+ OutputAnalyzer output = runTestWithOptions (
+ new String[] { gc.getSwitchName(), "-XX:+GCTrimNativeHeap" },
+ new String[] { "true" /* full gc */, String.valueOf(sleeptime_secs * 1000) /* ms after peak */ }
+ );
+ // With default interval time of 30 seconds, auto trimming should never kick in, so the only
+ // log line we expect to see is the one from the full-gc induced trim.
+ parseOutputAndLookForNegativeTrim(output, 1, 1);
+ // For GCs that use the NativeTrimmer, we want to see the NativeTrimmer paused during the GC, as well as
+ // started and shut down properly.
+ if (usesNativeTrimmer(gc)) {
+ output.shouldContain("NativeTrimmer started");
+ output.shouldContain("NativeTrimmer paused");
+ output.shouldContain("NativeTrimmer unpaused");
+ output.shouldContain("NativeTrimmer stopped");
+ } else {
+ output.shouldNotContain("NativeTrimmer");
+ }
+ }
+
+ // Test that GCTrimNativeHeap=1 causes a trim-native automatically, without GC (for now, shenandoah only)
+ static private final void testAuto(GC gc) throws Exception {
+ System.out.println("testAuto");
+ long t1 = System.currentTimeMillis();
+ int sleeptime_secs = 4;
+ OutputAnalyzer output = runTestWithOptions (
+ new String[] { gc.getSwitchName(), "-XX:+GCTrimNativeHeap", "-XX:GCTrimNativeHeapInterval=1" },
+ new String[] { "false" /* full gc */, String.valueOf(sleeptime_secs * 1000) /* ms after peak */ }
+ );
+ long t2 = System.currentTimeMillis();
+ int runtime_s = (int)((t2 - t1) / 1000);
+ // With an interval time of 1 second and a runtime of 6..x seconds we expect to see x log lines (+- fudge factor).
+ parseOutputAndLookForNegativeTrim(output, runtime_s - 4, runtime_s + 2);
+ }
+
+ // Test that trim-native correctly honors interval
+ static private final void testAutoWithHighInterval(GC gc) throws Exception {
+ // We pass a very high interval. This should disable the feature for this short-lived test, we should see no trim
+ System.out.println("testAutoWithHighInterval");
+ OutputAnalyzer output = runTestWithOptions (
+ new String[] { gc.getSwitchName(), "-XX:+GCTrimNativeHeap", "-XX:GCTrimNativeHeapInterval=30" },
+ new String[] { "false" /* full gc */, "6000" /* ms after peak */ }
+ );
+ output.shouldNotContain("Trim native heap");
+ }
+
+ // Test that trim-native correctly honors interval
+ static private final void testAutoWithZeroInterval(GC gc) throws Exception {
+ // We pass a very high interval. This should disable the feature for this short-lived test, we should see no trim
+ System.out.println("testAutoWithHighInterval");
+ OutputAnalyzer output = runTestWithOptions (
+ new String[] { gc.getSwitchName(), "-XX:+GCTrimNativeHeap", "-XX:GCTrimNativeHeapInterval=0" },
+ new String[] { "false" /* full gc */, "6000" /* ms after peak */ }
+ );
+ output.shouldNotContain("Trim native heap");
+ }
+
+ // Test that trim-native gets disabled on platforms that don't support it.
+ static private final void testOffOnNonCompliantPlatforms() throws Exception {
+ // Logic is shared, so no need to test with every GC. Just use the default GC.
+ System.out.println("testOffOnNonCompliantPlatforms");
+ OutputAnalyzer output = runTestWithOptions (
+ new String[] { "-XX:+GCTrimNativeHeap" },
+ new String[] { "true" /* full gc */, "2000" /* ms after peak */ }
+ );
+ output.shouldContain("GCTrimNativeHeap disabled");
+ output.shouldNotContain("Trim native heap");
+ }
+
+ // Test that GCTrimNativeHeap=0 switches trim-native off
+ static private final void testOffExplicit() throws Exception {
+ // Logic is shared, so no need to test with every GC. Just use the default GC.
+ System.out.println("testOffExplicit");
+ OutputAnalyzer output = runTestWithOptions (
+ new String[] { "-XX:-GCTrimNativeHeap" },
+ new String[] { "true" /* full gc */, "2000" /* ms after peak */ }
+ );
+ output.shouldNotContain("Trim native heap");
+ }
+
+ // Test that trim-native is disabled by default
+ static private final void testOffByDefault() throws Exception {
+ // Logic is shared, so no need to test with every GC. Just use the default GC.
+ System.out.println("testOffByDefault");
+ OutputAnalyzer output = runTestWithOptions (
+ new String[] { },
+ new String[] { "true" /* full gc */, "2000" /* ms after peak */ }
+ );
+ output.shouldNotContain("Trim native heap");
+ }
+
+ public static void main(String[] args) throws Exception {
+
+ if (args.length == 0) {
+ throw new RuntimeException("Argument error");
+ }
+
+ if (args[0].equals("RUN")) {
+ boolean doFullGC = Boolean.parseBoolean(args[1]);
+
+ System.out.println("Will spike now...");
+ Field field = Unsafe.class.getDeclaredField("theUnsafe");
+ field.setAccessible(true);
+ Unsafe unsafe = (Unsafe) field.get(null);
+ for (int i = 0; i < numAllocations; i++) {
+ ptrs[i] = unsafe.allocateMemory(szAllocations);
+ unsafe.putByte(ptrs[i], (byte)0);
+ unsafe.putByte(ptrs[i] + szAllocations / 2, (byte)0);
+ }
+ for (int i = 0; i < numAllocations; i++) {
+ unsafe.freeMemory(ptrs[i]);
+ }
+ System.out.println("Done spiking.");
+
+ if (doFullGC) {
+ System.out.println("GC...");
+ System.gc();
+ }
+
+ // give GC time to react
+ int time = Integer.parseInt(args[2]);
+ System.out.println("Sleeping...");
+ Thread.sleep(time);
+ System.out.println("Done.");
+
+ return;
+
+ } else if (args[0].equals("test-fullgc")) {
+ final GC gc = GC.valueOf(args[1]);
+ testWithFullGC(gc);
+ } else if (args[0].equals("test-auto")) {
+ final GC gc = GC.valueOf(args[1]);
+ testAuto(gc);
+ } else if (args[0].equals("test-auto-high-interval")) {
+ final GC gc = GC.valueOf(args[1]);
+ testAutoWithHighInterval(gc);
+ } else if (args[0].equals("test-auto-zero-interval")) {
+ final GC gc = GC.valueOf(args[1]);
+ testAutoWithZeroInterval(gc);
+ } else if (args[0].equals("test-off-explicit")) {
+ testOffExplicit();
+ } else if (args[0].equals("test-off-by-default")) {
+ testOffByDefault();
+ } else if (args[0].equals("test-off-on-other-platforms")) {
+ testOffOnNonCompliantPlatforms();
+ } else {
+ throw new RuntimeException("Invalid test " + args[0]);
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/hotspot/test/serviceability/dcmd/TrimLibcHeapTest.java b/hotspot/test/serviceability/dcmd/TrimLibcHeapTest.java
index 0fe8e35..131fa4c 100644
--- a/hotspot/test/serviceability/dcmd/TrimLibcHeapTest.java
+++ b/hotspot/test/serviceability/dcmd/TrimLibcHeapTest.java
@@ -29,7 +29,7 @@ import com.oracle.java.testlibrary.*;
* @test
* @summary Test of diagnostic command VM.trim_libc_heap
* @library /testlibrary
- * @requires os.family == "linux"
+ * @requires os.family=="linux"
* @modules java.base/jdk.internal.misc
* java.compiler
* java.management
@@ -40,10 +40,7 @@ public class TrimLibcHeapTest {
public void run(CommandExecutor executor) {
OutputAnalyzer output = executor.execute("System.trim_native_heap");
output.reportDiagnosticSummary();
- output.shouldMatch("(Done|Not available)"); // Not available could happen on Linux + non-glibc (eg. muslc)
- if (output.firstMatch("Done") != null) {
- output.shouldMatch("(Virtual size before|RSS before|Swap before|No details available)");
- }
+ output.shouldMatch(".*Trim native heap: RSS\\+Swap: \\d+[BKM]->\\d+[BKM].*");
}
@Test
--
1.8.3.1