Skip to content

Commit 6096dd9

Browse files
committedJul 22, 2021
8268893: jcmd to trim the glibc heap
Reviewed-by: simonis, dholmes
1 parent c36755d commit 6096dd9

File tree

6 files changed

+239
-26
lines changed

6 files changed

+239
-26
lines changed
 

‎src/hotspot/os/linux/os_linux.cpp

+32-25
Original file line numberDiff line numberDiff line change
@@ -2148,44 +2148,51 @@ void os::Linux::print_system_memory_info(outputStream* st) {
21482148
"/sys/kernel/mm/transparent_hugepage/defrag", st);
21492149
}
21502150

2151-
void os::Linux::print_process_memory_info(outputStream* st) {
2152-
2153-
st->print_cr("Process Memory:");
2154-
2155-
// Print virtual and resident set size; peak values; swap; and for
2156-
// rss its components if the kernel is recent enough.
2157-
ssize_t vmsize = -1, vmpeak = -1, vmswap = -1,
2158-
vmrss = -1, vmhwm = -1, rssanon = -1, rssfile = -1, rssshmem = -1;
2159-
const int num_values = 8;
2160-
int num_found = 0;
2151+
bool os::Linux::query_process_memory_info(os::Linux::meminfo_t* info) {
21612152
FILE* f = ::fopen("/proc/self/status", "r");
2153+
const int num_values = sizeof(os::Linux::meminfo_t) / sizeof(size_t);
2154+
int num_found = 0;
21622155
char buf[256];
2156+
info->vmsize = info->vmpeak = info->vmrss = info->vmhwm = info->vmswap =
2157+
info->rssanon = info->rssfile = info->rssshmem = -1;
21632158
if (f != NULL) {
21642159
while (::fgets(buf, sizeof(buf), f) != NULL && num_found < num_values) {
2165-
if ( (vmsize == -1 && sscanf(buf, "VmSize: " SSIZE_FORMAT " kB", &vmsize) == 1) ||
2166-
(vmpeak == -1 && sscanf(buf, "VmPeak: " SSIZE_FORMAT " kB", &vmpeak) == 1) ||
2167-
(vmswap == -1 && sscanf(buf, "VmSwap: " SSIZE_FORMAT " kB", &vmswap) == 1) ||
2168-
(vmhwm == -1 && sscanf(buf, "VmHWM: " SSIZE_FORMAT " kB", &vmhwm) == 1) ||
2169-
(vmrss == -1 && sscanf(buf, "VmRSS: " SSIZE_FORMAT " kB", &vmrss) == 1) ||
2170-
(rssanon == -1 && sscanf(buf, "RssAnon: " SSIZE_FORMAT " kB", &rssanon) == 1) ||
2171-
(rssfile == -1 && sscanf(buf, "RssFile: " SSIZE_FORMAT " kB", &rssfile) == 1) ||
2172-
(rssshmem == -1 && sscanf(buf, "RssShmem: " SSIZE_FORMAT " kB", &rssshmem) == 1)
2160+
if ( (info->vmsize == -1 && sscanf(buf, "VmSize: " SSIZE_FORMAT " kB", &info->vmsize) == 1) ||
2161+
(info->vmpeak == -1 && sscanf(buf, "VmPeak: " SSIZE_FORMAT " kB", &info->vmpeak) == 1) ||
2162+
(info->vmswap == -1 && sscanf(buf, "VmSwap: " SSIZE_FORMAT " kB", &info->vmswap) == 1) ||
2163+
(info->vmhwm == -1 && sscanf(buf, "VmHWM: " SSIZE_FORMAT " kB", &info->vmhwm) == 1) ||
2164+
(info->vmrss == -1 && sscanf(buf, "VmRSS: " SSIZE_FORMAT " kB", &info->vmrss) == 1) ||
2165+
(info->rssanon == -1 && sscanf(buf, "RssAnon: " SSIZE_FORMAT " kB", &info->rssanon) == 1) || // Needs Linux 4.5
2166+
(info->rssfile == -1 && sscanf(buf, "RssFile: " SSIZE_FORMAT " kB", &info->rssfile) == 1) || // Needs Linux 4.5
2167+
(info->rssshmem == -1 && sscanf(buf, "RssShmem: " SSIZE_FORMAT " kB", &info->rssshmem) == 1) // Needs Linux 4.5
21732168
)
21742169
{
21752170
num_found ++;
21762171
}
21772172
}
21782173
fclose(f);
2174+
return true;
2175+
}
2176+
return false;
2177+
}
2178+
2179+
void os::Linux::print_process_memory_info(outputStream* st) {
21792180

2180-
st->print_cr("Virtual Size: " SSIZE_FORMAT "K (peak: " SSIZE_FORMAT "K)", vmsize, vmpeak);
2181-
st->print("Resident Set Size: " SSIZE_FORMAT "K (peak: " SSIZE_FORMAT "K)", vmrss, vmhwm);
2182-
if (rssanon != -1) { // requires kernel >= 4.5
2181+
st->print_cr("Process Memory:");
2182+
2183+
// Print virtual and resident set size; peak values; swap; and for
2184+
// rss its components if the kernel is recent enough.
2185+
meminfo_t info;
2186+
if (query_process_memory_info(&info)) {
2187+
st->print_cr("Virtual Size: " SSIZE_FORMAT "K (peak: " SSIZE_FORMAT "K)", info.vmsize, info.vmpeak);
2188+
st->print("Resident Set Size: " SSIZE_FORMAT "K (peak: " SSIZE_FORMAT "K)", info.vmrss, info.vmhwm);
2189+
if (info.rssanon != -1) { // requires kernel >= 4.5
21832190
st->print(" (anon: " SSIZE_FORMAT "K, file: " SSIZE_FORMAT "K, shmem: " SSIZE_FORMAT "K)",
2184-
rssanon, rssfile, rssshmem);
2191+
info.rssanon, info.rssfile, info.rssshmem);
21852192
}
21862193
st->cr();
2187-
if (vmswap != -1) { // requires kernel >= 2.6.34
2188-
st->print_cr("Swapped out: " SSIZE_FORMAT "K", vmswap);
2194+
if (info.vmswap != -1) { // requires kernel >= 2.6.34
2195+
st->print_cr("Swapped out: " SSIZE_FORMAT "K", info.vmswap);
21892196
}
21902197
} else {
21912198
st->print_cr("Could not open /proc/self/status to get process memory related information");
@@ -2206,7 +2213,7 @@ void os::Linux::print_process_memory_info(outputStream* st) {
22062213
struct glibc_mallinfo mi = _mallinfo();
22072214
total_allocated = (size_t)(unsigned)mi.uordblks;
22082215
// Since mallinfo members are int, glibc values may have wrapped. Warn about this.
2209-
might_have_wrapped = (vmrss * K) > UINT_MAX && (vmrss * K) > (total_allocated + UINT_MAX);
2216+
might_have_wrapped = (info.vmrss * K) > UINT_MAX && (info.vmrss * K) > (total_allocated + UINT_MAX);
22102217
}
22112218
if (_mallinfo2 != NULL || _mallinfo != NULL) {
22122219
st->print_cr("C-Heap outstanding allocations: " SIZE_FORMAT "K%s",

‎src/hotspot/os/linux/os_linux.hpp

+17
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,23 @@ class Linux {
174174
// Return the namespace pid if so, otherwise -1.
175175
static int get_namespace_pid(int vmid);
176176

177+
// Output structure for query_process_memory_info()
178+
struct meminfo_t {
179+
ssize_t vmsize; // current virtual size
180+
ssize_t vmpeak; // peak virtual size
181+
ssize_t vmrss; // current resident set size
182+
ssize_t vmhwm; // peak resident set size
183+
ssize_t vmswap; // swapped out
184+
ssize_t rssanon; // resident set size (anonymous mappings, needs 4.5)
185+
ssize_t rssfile; // resident set size (file mappings, needs 4.5)
186+
ssize_t rssshmem; // resident set size (shared mappings, needs 4.5)
187+
};
188+
189+
// Attempts to query memory information about the current process and return it in the output structure.
190+
// May fail (returns false) or succeed (returns true) but not all output fields are available; unavailable
191+
// fields will contain -1.
192+
static bool query_process_memory_info(meminfo_t* info);
193+
177194
// Stack repair handling
178195

179196
// none present
+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright (c) 2021 SAP SE. All rights reserved.
3+
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
4+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5+
*
6+
* This code is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 only, as
8+
* published by the Free Software Foundation.
9+
*
10+
* This code is distributed in the hope that it will be useful, but WITHOUT
11+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13+
* version 2 for more details (a copy is included in the LICENSE file that
14+
* accompanied this code).
15+
*
16+
* You should have received a copy of the GNU General Public License version
17+
* 2 along with this work; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19+
*
20+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21+
* or visit www.oracle.com if you need additional information or have any
22+
* questions.
23+
*
24+
*/
25+
26+
#include "precompiled.hpp"
27+
#include "logging/log.hpp"
28+
#include "runtime/os.hpp"
29+
#include "utilities/debug.hpp"
30+
#include "utilities/ostream.hpp"
31+
#include "trimCHeapDCmd.hpp"
32+
33+
#include <malloc.h>
34+
35+
void TrimCLibcHeapDCmd::execute(DCmdSource source, TRAPS) {
36+
#ifdef __GLIBC__
37+
stringStream ss_report(1024); // Note: before calling trim
38+
39+
os::Linux::meminfo_t info1;
40+
os::Linux::meminfo_t info2;
41+
// Query memory before...
42+
bool have_info1 = os::Linux::query_process_memory_info(&info1);
43+
44+
_output->print_cr("Attempting trim...");
45+
::malloc_trim(0);
46+
_output->print_cr("Done.");
47+
48+
// ...and after trim.
49+
bool have_info2 = os::Linux::query_process_memory_info(&info2);
50+
51+
// Print report both to output stream as well to UL
52+
bool wrote_something = false;
53+
if (have_info1 && have_info2) {
54+
if (info1.vmsize != -1 && info2.vmsize != -1) {
55+
ss_report.print_cr("Virtual size before: " SSIZE_FORMAT "k, after: " SSIZE_FORMAT "k, (" SSIZE_FORMAT "k)",
56+
info1.vmsize, info2.vmsize, (info2.vmsize - info1.vmsize));
57+
wrote_something = true;
58+
}
59+
if (info1.vmrss != -1 && info2.vmrss != -1) {
60+
ss_report.print_cr("RSS before: " SSIZE_FORMAT "k, after: " SSIZE_FORMAT "k, (" SSIZE_FORMAT "k)",
61+
info1.vmrss, info2.vmrss, (info2.vmrss - info1.vmrss));
62+
wrote_something = true;
63+
}
64+
if (info1.vmswap != -1 && info2.vmswap != -1) {
65+
ss_report.print_cr("Swap before: " SSIZE_FORMAT "k, after: " SSIZE_FORMAT "k, (" SSIZE_FORMAT "k)",
66+
info1.vmswap, info2.vmswap, (info2.vmswap - info1.vmswap));
67+
wrote_something = true;
68+
}
69+
}
70+
if (!wrote_something) {
71+
ss_report.print_raw("No details available.");
72+
}
73+
74+
_output->print_raw(ss_report.base());
75+
log_info(os)("malloc_trim:\n%s", ss_report.base());
76+
#else
77+
_output->print_cr("Not available.");
78+
#endif
79+
}
+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright (c) 2021 SAP SE. All rights reserved.
3+
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
4+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5+
*
6+
* This code is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 only, as
8+
* published by the Free Software Foundation.
9+
*
10+
* This code is distributed in the hope that it will be useful, but WITHOUT
11+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13+
* version 2 for more details (a copy is included in the LICENSE file that
14+
* accompanied this code).
15+
*
16+
* You should have received a copy of the GNU General Public License version
17+
* 2 along with this work; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19+
*
20+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21+
* or visit www.oracle.com if you need additional information or have any
22+
* questions.
23+
*
24+
*/
25+
26+
#ifndef OS_LINUX_TRIMCHEAPDCMD_HPP
27+
#define OS_LINUX_TRIMCHEAPDCMD_HPP
28+
29+
#include "services/diagnosticCommand.hpp"
30+
31+
class outputStream;
32+
33+
class TrimCLibcHeapDCmd : public DCmd {
34+
public:
35+
TrimCLibcHeapDCmd(outputStream* output, bool heap) : DCmd(output, heap) {}
36+
static const char* name() {
37+
return "System.trim_native_heap";
38+
}
39+
static const char* description() {
40+
return "Attempts to free up memory by trimming the C-heap.";
41+
}
42+
static const char* impact() {
43+
return "Low";
44+
}
45+
static const JavaPermission permission() {
46+
JavaPermission p = { "java.lang.management.ManagementPermission", "control", NULL };
47+
return p;
48+
}
49+
virtual void execute(DCmdSource source, TRAPS);
50+
};
51+
52+
#endif // OS_LINUX_TRIMCHEAPDCMD_HPP

‎src/hotspot/share/services/diagnosticCommand.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@
5858
#include "utilities/events.hpp"
5959
#include "utilities/formatBuffer.hpp"
6060
#include "utilities/macros.hpp"
61-
61+
#ifdef LINUX
62+
#include "trimCHeapDCmd.hpp"
63+
#endif
6264

6365
static void loadAgentModule(TRAPS) {
6466
ResourceMark rm(THREAD);
@@ -118,6 +120,7 @@ void DCmdRegistrant::register_dcmds(){
118120
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CodeCacheDCmd>(full_export, true, false));
119121
#ifdef LINUX
120122
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<PerfMapDCmd>(full_export, true, false));
123+
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<TrimCLibcHeapDCmd>(full_export, true, false));
121124
#endif // LINUX
122125
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<TouchedMethodsDCmd>(full_export, true, false));
123126
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CodeHeapAnalyticsDCmd>(full_export, true, false));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright (c) 2021 SAP SE. All rights reserved.
3+
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
4+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5+
*
6+
* This code is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 only, as
8+
* published by the Free Software Foundation.
9+
*
10+
* This code is distributed in the hope that it will be useful, but WITHOUT
11+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13+
* version 2 for more details (a copy is included in the LICENSE file that
14+
* accompanied this code).
15+
*
16+
* You should have received a copy of the GNU General Public License version
17+
* 2 along with this work; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19+
*
20+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21+
* or visit www.oracle.com if you need additional information or have any
22+
* questions.
23+
*/
24+
25+
import org.testng.annotations.Test;
26+
import jdk.test.lib.dcmd.CommandExecutor;
27+
import jdk.test.lib.dcmd.JMXExecutor;
28+
import jdk.test.lib.process.OutputAnalyzer;
29+
30+
/*
31+
* @test
32+
* @summary Test of diagnostic command VM.trim_libc_heap
33+
* @library /test/lib
34+
* @requires os.family == "linux"
35+
* @modules java.base/jdk.internal.misc
36+
* java.compiler
37+
* java.management
38+
* jdk.internal.jvmstat/sun.jvmstat.monitor
39+
* @run testng TrimLibcHeapTest
40+
*/
41+
public class TrimLibcHeapTest {
42+
public void run(CommandExecutor executor) {
43+
OutputAnalyzer output = executor.execute("System.trim_native_heap");
44+
output.reportDiagnosticSummary();
45+
output.shouldMatch("(Done|Not available)"); // Not available could happen on Linux + non-glibc (eg. muslc)
46+
if (output.firstMatch("Done") != null) {
47+
output.shouldMatch("(Virtual size before|RSS before|Swap before|No details available)");
48+
}
49+
}
50+
51+
@Test
52+
public void jmx() {
53+
run(new JMXExecutor());
54+
}
55+
}

0 commit comments

Comments
 (0)
Please sign in to comment.