Projects
openEuler:24.03:SP1:Everything
openjdk-1.8.0
_service:tar_scm:8293114-GC-should-trim-the-nat...
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File _service:tar_scm:8293114-GC-should-trim-the-native-heap-and-bug-fix.patch of Package openjdk-1.8.0
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
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.
浙ICP备2022010568号-2