Skip to content
Permalink
Browse files
8268893: jcmd to trim the glibc heap
Backport-of: 6096dd9765eaf280890f65c0ff1ab64864b9316a
  • Loading branch information
tstuefe committed Sep 28, 2021
1 parent c4dfa57 commit d93500168cd120165fedb9609fdf2e10458976dd
Showing 6 changed files with 239 additions and 26 deletions.
@@ -2137,44 +2137,51 @@ void os::Linux::print_system_memory_info(outputStream* st) {
"/sys/kernel/mm/transparent_hugepage/defrag", st);
}

void os::Linux::print_process_memory_info(outputStream* st) {

st->print_cr("Process Memory:");

// Print virtual and resident set size; peak values; swap; and for
// rss its components if the kernel is recent enough.
ssize_t vmsize = -1, vmpeak = -1, vmswap = -1,
vmrss = -1, vmhwm = -1, rssanon = -1, rssfile = -1, rssshmem = -1;
const int num_values = 8;
int num_found = 0;
bool os::Linux::query_process_memory_info(os::Linux::meminfo_t* info) {
FILE* f = ::fopen("/proc/self/status", "r");
const int num_values = sizeof(os::Linux::meminfo_t) / sizeof(size_t);
int num_found = 0;
char buf[256];
info->vmsize = info->vmpeak = info->vmrss = info->vmhwm = info->vmswap =
info->rssanon = info->rssfile = info->rssshmem = -1;
if (f != NULL) {
while (::fgets(buf, sizeof(buf), f) != NULL && num_found < num_values) {
if ( (vmsize == -1 && sscanf(buf, "VmSize: " SSIZE_FORMAT " kB", &vmsize) == 1) ||
(vmpeak == -1 && sscanf(buf, "VmPeak: " SSIZE_FORMAT " kB", &vmpeak) == 1) ||
(vmswap == -1 && sscanf(buf, "VmSwap: " SSIZE_FORMAT " kB", &vmswap) == 1) ||
(vmhwm == -1 && sscanf(buf, "VmHWM: " SSIZE_FORMAT " kB", &vmhwm) == 1) ||
(vmrss == -1 && sscanf(buf, "VmRSS: " SSIZE_FORMAT " kB", &vmrss) == 1) ||
(rssanon == -1 && sscanf(buf, "RssAnon: " SSIZE_FORMAT " kB", &rssanon) == 1) ||
(rssfile == -1 && sscanf(buf, "RssFile: " SSIZE_FORMAT " kB", &rssfile) == 1) ||
(rssshmem == -1 && sscanf(buf, "RssShmem: " SSIZE_FORMAT " kB", &rssshmem) == 1)
if ( (info->vmsize == -1 && sscanf(buf, "VmSize: " SSIZE_FORMAT " kB", &info->vmsize) == 1) ||
(info->vmpeak == -1 && sscanf(buf, "VmPeak: " SSIZE_FORMAT " kB", &info->vmpeak) == 1) ||
(info->vmswap == -1 && sscanf(buf, "VmSwap: " SSIZE_FORMAT " kB", &info->vmswap) == 1) ||
(info->vmhwm == -1 && sscanf(buf, "VmHWM: " SSIZE_FORMAT " kB", &info->vmhwm) == 1) ||
(info->vmrss == -1 && sscanf(buf, "VmRSS: " SSIZE_FORMAT " kB", &info->vmrss) == 1) ||
(info->rssanon == -1 && sscanf(buf, "RssAnon: " SSIZE_FORMAT " kB", &info->rssanon) == 1) || // Needs Linux 4.5
(info->rssfile == -1 && sscanf(buf, "RssFile: " SSIZE_FORMAT " kB", &info->rssfile) == 1) || // Needs Linux 4.5
(info->rssshmem == -1 && sscanf(buf, "RssShmem: " SSIZE_FORMAT " kB", &info->rssshmem) == 1) // Needs Linux 4.5
)
{
num_found ++;
}
}
fclose(f);
return true;
}
return false;
}

void os::Linux::print_process_memory_info(outputStream* st) {

st->print_cr("Virtual Size: " SSIZE_FORMAT "K (peak: " SSIZE_FORMAT "K)", vmsize, vmpeak);
st->print("Resident Set Size: " SSIZE_FORMAT "K (peak: " SSIZE_FORMAT "K)", vmrss, vmhwm);
if (rssanon != -1) { // requires kernel >= 4.5
st->print_cr("Process Memory:");

// Print virtual and resident set size; peak values; swap; and for
// rss its components if the kernel is recent enough.
meminfo_t info;
if (query_process_memory_info(&info)) {
st->print_cr("Virtual Size: " SSIZE_FORMAT "K (peak: " SSIZE_FORMAT "K)", info.vmsize, info.vmpeak);
st->print("Resident Set Size: " SSIZE_FORMAT "K (peak: " SSIZE_FORMAT "K)", info.vmrss, info.vmhwm);
if (info.rssanon != -1) { // requires kernel >= 4.5
st->print(" (anon: " SSIZE_FORMAT "K, file: " SSIZE_FORMAT "K, shmem: " SSIZE_FORMAT "K)",
rssanon, rssfile, rssshmem);
info.rssanon, info.rssfile, info.rssshmem);
}
st->cr();
if (vmswap != -1) { // requires kernel >= 2.6.34
st->print_cr("Swapped out: " SSIZE_FORMAT "K", vmswap);
if (info.vmswap != -1) { // requires kernel >= 2.6.34
st->print_cr("Swapped out: " SSIZE_FORMAT "K", info.vmswap);
}
} else {
st->print_cr("Could not open /proc/self/status to get process memory related information");
@@ -2195,7 +2202,7 @@ void os::Linux::print_process_memory_info(outputStream* st) {
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 = (vmrss * K) > UINT_MAX && (vmrss * K) > (total_allocated + UINT_MAX);
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",
@@ -174,6 +174,23 @@ class Linux {
// Return the namespace pid if so, otherwise -1.
static int get_namespace_pid(int vmid);

// Output structure for query_process_memory_info()
struct meminfo_t {
ssize_t vmsize; // current virtual size
ssize_t vmpeak; // peak virtual size
ssize_t vmrss; // current resident set size
ssize_t vmhwm; // peak resident set size
ssize_t vmswap; // swapped out
ssize_t rssanon; // resident set size (anonymous mappings, needs 4.5)
ssize_t rssfile; // resident set size (file mappings, needs 4.5)
ssize_t rssshmem; // resident set size (shared mappings, needs 4.5)
};

// Attempts to query memory information about the current process and return it in the output structure.
// May fail (returns false) or succeed (returns true) but not all output fields are available; unavailable
// fields will contain -1.
static bool query_process_memory_info(meminfo_t* info);

// Stack repair handling

// none present
@@ -0,0 +1,79 @@
/*
* Copyright (c) 2021 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.
*
* 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 "logging/log.hpp"
#include "runtime/os.hpp"
#include "utilities/debug.hpp"
#include "utilities/ostream.hpp"
#include "trimCHeapDCmd.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 (!wrote_something) {
ss_report.print_raw("No details available.");
}

_output->print_raw(ss_report.base());
log_info(os)("malloc_trim:\n%s", ss_report.base());
#else
_output->print_cr("Not available.");
#endif
}
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2021 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.
*
* 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 OS_LINUX_TRIMCHEAPDCMD_HPP
#define OS_LINUX_TRIMCHEAPDCMD_HPP

#include "services/diagnosticCommand.hpp"

class outputStream;

class TrimCLibcHeapDCmd : public DCmd {
public:
TrimCLibcHeapDCmd(outputStream* output, bool heap) : DCmd(output, heap) {}
static const char* name() {
return "System.trim_native_heap";
}
static const char* description() {
return "Attempts to free up memory by trimming the C-heap.";
}
static const char* impact() {
return "Low";
}
static const JavaPermission permission() {
JavaPermission p = { "java.lang.management.ManagementPermission", "control", NULL };
return p;
}
virtual void execute(DCmdSource source, TRAPS);
};

#endif // OS_LINUX_TRIMCHEAPDCMD_HPP
@@ -58,7 +58,9 @@
#include "utilities/events.hpp"
#include "utilities/formatBuffer.hpp"
#include "utilities/macros.hpp"

#ifdef LINUX
#include "trimCHeapDCmd.hpp"
#endif

static void loadAgentModule(TRAPS) {
ResourceMark rm(THREAD);
@@ -118,6 +120,7 @@ void DCmdRegistrant::register_dcmds(){
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CodeCacheDCmd>(full_export, true, false));
#ifdef LINUX
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<PerfMapDCmd>(full_export, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<TrimCLibcHeapDCmd>(full_export, true, false));
#endif // LINUX
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<TouchedMethodsDCmd>(full_export, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CodeHeapAnalyticsDCmd>(full_export, true, false));
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2021 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.
*
* 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.
*/

import org.testng.annotations.Test;
import jdk.test.lib.dcmd.CommandExecutor;
import jdk.test.lib.dcmd.JMXExecutor;
import jdk.test.lib.process.OutputAnalyzer;

/*
* @test
* @summary Test of diagnostic command VM.trim_libc_heap
* @library /test/lib
* @requires os.family == "linux"
* @modules java.base/jdk.internal.misc
* java.compiler
* java.management
* jdk.internal.jvmstat/sun.jvmstat.monitor
* @run testng TrimLibcHeapTest
*/
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)");
}
}

@Test
public void jmx() {
run(new JMXExecutor());
}
}

1 comment on commit d935001

@openjdk-notifier
Copy link

@openjdk-notifier openjdk-notifier bot commented on d935001 Sep 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.