1504 lines
57 KiB
Diff
1504 lines
57 KiB
Diff
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,8136854:G1 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
|