8291878: NMT: Malloc limits

Reviewed-by: kvn, shade
This commit is contained in:
Thomas Stuefe 2022-08-24 07:58:54 +00:00
parent ad2e0c4df0
commit fa5cc4cc8e
9 changed files with 475 additions and 7 deletions

View file

@ -4408,3 +4408,78 @@ bool Arguments::copy_expand_pid(const char* src, size_t srclen,
*b = '\0'; *b = '\0';
return (p == src_end); // return false if not all of the source was copied return (p == src_end); // return false if not all of the source was copied
} }
bool Arguments::parse_malloc_limit_size(const char* s, size_t* out) {
julong limit = 0;
Arguments::ArgsRange range = parse_memory_size(s, &limit, 1, SIZE_MAX);
switch (range) {
case ArgsRange::arg_in_range:
*out = (size_t)limit;
return true;
case ArgsRange::arg_too_big: // only possible on 32-bit
vm_exit_during_initialization("MallocLimit: too large", s);
break;
case ArgsRange::arg_too_small:
vm_exit_during_initialization("MallocLimit: limit must be > 0");
break;
default:
break;
}
return false;
}
// Helper for parse_malloc_limits
void Arguments::parse_single_category_limit(char* expression, size_t limits[mt_number_of_types]) {
// <category>:<limit>
char* colon = ::strchr(expression, ':');
if (colon == nullptr) {
vm_exit_during_initialization("MallocLimit: colon missing", expression);
}
*colon = '\0';
MEMFLAGS f = NMTUtil::string_to_flag(expression);
if (f == mtNone) {
vm_exit_during_initialization("MallocLimit: invalid nmt category", expression);
}
if (parse_malloc_limit_size(colon + 1, limits + (int)f) == false) {
vm_exit_during_initialization("Invalid MallocLimit size", colon + 1);
}
}
void Arguments::parse_malloc_limits(size_t* total_limit, size_t limits[mt_number_of_types]) {
// Reset output to 0
*total_limit = 0;
for (int i = 0; i < mt_number_of_types; i ++) {
limits[i] = 0;
}
// We are done if the option is not given.
if (MallocLimit == nullptr) {
return;
}
// Global form?
if (parse_malloc_limit_size(MallocLimit, total_limit)) {
return;
}
// No. So it must be in category-specific form: MallocLimit=<nmt category>:<size>[,<nmt category>:<size> ..]
char* copy = os::strdup(MallocLimit);
if (copy == nullptr) {
vm_exit_out_of_memory(strlen(MallocLimit), OOM_MALLOC_ERROR, "MallocLimit");
}
char* p = copy, *q;
do {
q = p;
p = ::strchr(q, ',');
if (p != nullptr) {
*p = '\0';
p ++;
}
parse_single_category_limit(q, limits);
} while (p != nullptr);
os::free(copy);
}

View file

@ -479,6 +479,10 @@ class Arguments : AllStatic {
char** base_archive_path, char** base_archive_path,
char** top_archive_path) NOT_CDS_RETURN; char** top_archive_path) NOT_CDS_RETURN;
// Helpers for parse_malloc_limits
static bool parse_malloc_limit_size(const char* s, size_t* out);
static void parse_single_category_limit(char* expression, size_t limits[mt_number_of_types]);
public: public:
static int num_archives(const char* archive_path) NOT_CDS_RETURN_(0); static int num_archives(const char* archive_path) NOT_CDS_RETURN_(0);
// Parses the arguments, first phase // Parses the arguments, first phase
@ -653,6 +657,16 @@ class Arguments : AllStatic {
assert(Arguments::is_dumping_archive(), "dump time only"); assert(Arguments::is_dumping_archive(), "dump time only");
} }
// Parse diagnostic NMT switch "MallocLimit" and return the found limits.
// 1) If option is not given, it will set all limits to 0 (aka "no limit").
// 2) If option is given in the global form (-XX:MallocLimit=<size>), it
// will return the size in *total_limit.
// 3) If option is given in its per-NMT-category form (-XX:MallocLimit=<category>:<size>[,<category>:<size>]),
// it will return all found limits in the limits array.
// 4) If option is malformed, it will exit the VM.
// For (2) and (3), limits not affected by the switch will be set to 0.
static void parse_malloc_limits(size_t* total_limit, size_t limits[mt_number_of_types]);
DEBUG_ONLY(static bool verify_special_jvm_flags(bool check_globals);) DEBUG_ONLY(static bool verify_special_jvm_flags(bool check_globals);)
}; };

View file

@ -1369,6 +1369,16 @@ const int ObjectAlignmentInBytes = 8;
"allocate (for testing only)") \ "allocate (for testing only)") \
range(0, max_uintx) \ range(0, max_uintx) \
\ \
product(ccstr, MallocLimit, nullptr, DIAGNOSTIC, \
"Limit malloc allocation size from VM. Reaching the limit will " \
"trigger a fatal error. This feature requires " \
"NativeMemoryTracking=summary or NativeMemoryTracking=detail." \
"Usage:" \
"- MallocLimit=<size> to set a total limit. " \
"- MallocLimit=<NMT category>:<size>[,<NMT category>:<size>...] " \
" to set one or more category-specific limits." \
"Example: -XX:MallocLimit=compiler:500m") \
\
product(intx, TypeProfileWidth, 2, \ product(intx, TypeProfileWidth, 2, \
"Number of receiver types to record in call/cast profile") \ "Number of receiver types to record in call/cast profile") \
range(0, 8) \ range(0, 8) \

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2022 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -23,6 +24,8 @@
*/ */
#include "precompiled.hpp" #include "precompiled.hpp"
#include "logging/log.hpp"
#include "runtime/arguments.hpp"
#include "runtime/os.hpp" #include "runtime/os.hpp"
#include "runtime/safefetch.hpp" #include "runtime/safefetch.hpp"
#include "services/mallocHeader.inline.hpp" #include "services/mallocHeader.inline.hpp"
@ -31,10 +34,13 @@
#include "services/memTracker.hpp" #include "services/memTracker.hpp"
#include "utilities/debug.hpp" #include "utilities/debug.hpp"
#include "utilities/ostream.hpp" #include "utilities/ostream.hpp"
#include "utilities/vmError.hpp"
#include "jvm_io.h" #include "jvm_io.h"
size_t MallocMemorySummary::_snapshot[CALC_OBJ_SIZE_IN_TYPE(MallocMemorySnapshot, size_t)]; size_t MallocMemorySummary::_snapshot[CALC_OBJ_SIZE_IN_TYPE(MallocMemorySnapshot, size_t)];
size_t MallocMemorySummary::_limits_per_category[mt_number_of_types] = { 0 };
size_t MallocMemorySummary::_total_limit = 0;
#ifdef ASSERT #ifdef ASSERT
void MemoryCounter::update_peak_count(size_t count) { void MemoryCounter::update_peak_count(size_t count) {
@ -88,6 +94,59 @@ void MallocMemorySummary::initialize() {
assert(sizeof(_snapshot) >= sizeof(MallocMemorySnapshot), "Sanity Check"); assert(sizeof(_snapshot) >= sizeof(MallocMemorySnapshot), "Sanity Check");
// Uses placement new operator to initialize static area. // Uses placement new operator to initialize static area.
::new ((void*)_snapshot)MallocMemorySnapshot(); ::new ((void*)_snapshot)MallocMemorySnapshot();
initialize_limit_handling();
}
void MallocMemorySummary::initialize_limit_handling() {
// Initialize limit handling.
Arguments::parse_malloc_limits(&_total_limit, _limits_per_category);
if (_total_limit > 0) {
log_info(nmt)("MallocLimit: total limit: " SIZE_FORMAT "%s",
byte_size_in_proper_unit(_total_limit),
proper_unit_for_byte_size(_total_limit));
} else {
for (int i = 0; i < mt_number_of_types; i ++) {
size_t catlim = _limits_per_category[i];
if (catlim > 0) {
log_info(nmt)("MallocLimit: category \"%s\" limit: " SIZE_FORMAT "%s",
NMTUtil::flag_to_name((MEMFLAGS)i),
byte_size_in_proper_unit(catlim),
proper_unit_for_byte_size(catlim));
}
}
}
}
void MallocMemorySummary::total_limit_reached(size_t size, size_t limit) {
// Assert in both debug and release, but allow error reporting to malloc beyond limits.
if (!VMError::is_error_reported()) {
fatal("MallocLimit: reached limit (size: " SIZE_FORMAT ", limit: " SIZE_FORMAT ") ",
size, limit);
}
}
void MallocMemorySummary::category_limit_reached(size_t size, size_t limit, MEMFLAGS flag) {
// Assert in both debug and release, but allow error reporting to malloc beyond limits.
if (!VMError::is_error_reported()) {
fatal("MallocLimit: category \"%s\" reached limit (size: " SIZE_FORMAT ", limit: " SIZE_FORMAT ") ",
NMTUtil::flag_to_name(flag), size, limit);
}
}
void MallocMemorySummary::print_limits(outputStream* st) {
if (_total_limit != 0) {
st->print("MallocLimit: " SIZE_FORMAT, _total_limit);
} else {
bool first = true;
for (int i = 0; i < mt_number_of_types; i ++) {
if (_limits_per_category[i] > 0) {
st->print("%s%s:" SIZE_FORMAT, (first ? "MallocLimit: " : ", "),
NMTUtil::flag_to_name((MEMFLAGS)i), _limits_per_category[i]);
first = false;
}
}
}
} }
bool MallocTracker::initialize(NMT_TrackingLevel level) { bool MallocTracker::initialize(NMT_TrackingLevel level) {

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2022 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -196,12 +197,41 @@ class MallocMemorySummary : AllStatic {
// Reserve memory for placement of MallocMemorySnapshot object // Reserve memory for placement of MallocMemorySnapshot object
static size_t _snapshot[CALC_OBJ_SIZE_IN_TYPE(MallocMemorySnapshot, size_t)]; static size_t _snapshot[CALC_OBJ_SIZE_IN_TYPE(MallocMemorySnapshot, size_t)];
// Malloc Limit handling (-XX:MallocLimit)
static size_t _limits_per_category[mt_number_of_types];
static size_t _total_limit;
static void initialize_limit_handling();
static void total_limit_reached(size_t size, size_t limit);
static void category_limit_reached(size_t size, size_t limit, MEMFLAGS flag);
static void check_limits_after_allocation(MEMFLAGS flag) {
// We can only either have a total limit or category specific limits,
// not both.
if (_total_limit != 0) {
size_t s = as_snapshot()->total();
if (s > _total_limit) {
total_limit_reached(s, _total_limit);
}
} else {
size_t per_cat_limit = _limits_per_category[(int)flag];
if (per_cat_limit > 0) {
const MallocMemory* mm = as_snapshot()->by_type(flag);
size_t s = mm->malloc_size() + mm->arena_size();
if (s > per_cat_limit) {
category_limit_reached(s, per_cat_limit, flag);
}
}
}
}
public: public:
static void initialize(); static void initialize();
static inline void record_malloc(size_t size, MEMFLAGS flag) { static inline void record_malloc(size_t size, MEMFLAGS flag) {
as_snapshot()->by_type(flag)->record_malloc(size); as_snapshot()->by_type(flag)->record_malloc(size);
as_snapshot()->_all_mallocs.allocate(size); as_snapshot()->_all_mallocs.allocate(size);
check_limits_after_allocation(flag);
} }
static inline void record_free(size_t size, MEMFLAGS flag) { static inline void record_free(size_t size, MEMFLAGS flag) {
@ -219,6 +249,7 @@ class MallocMemorySummary : AllStatic {
static inline void record_arena_size_change(ssize_t size, MEMFLAGS flag) { static inline void record_arena_size_change(ssize_t size, MEMFLAGS flag) {
as_snapshot()->by_type(flag)->record_arena_size_change(size); as_snapshot()->by_type(flag)->record_arena_size_change(size);
check_limits_after_allocation(flag);
} }
static void snapshot(MallocMemorySnapshot* s) { static void snapshot(MallocMemorySnapshot* s) {
@ -234,6 +265,8 @@ class MallocMemorySummary : AllStatic {
static MallocMemorySnapshot* as_snapshot() { static MallocMemorySnapshot* as_snapshot() {
return (MallocMemorySnapshot*)_snapshot; return (MallocMemorySnapshot*)_snapshot;
} }
static void print_limits(outputStream* st);
}; };
// Main class called from MemTracker to track malloc activities // Main class called from MemTracker to track malloc activities

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2028, 2022 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -73,6 +74,10 @@ void MemTracker::initialize() {
log_warning(nmt)("NMT initialization failed. NMT disabled."); log_warning(nmt)("NMT initialization failed. NMT disabled.");
return; return;
} }
} else {
if (MallocLimit != nullptr) {
warning("MallocLimit will be ignored since NMT is disabled.");
}
} }
NMTPreInit::pre_to_post(); NMTPreInit::pre_to_post();
@ -110,6 +115,7 @@ void MemTracker::error_report(outputStream* output) {
report(true, output, MemReporterBase::default_scale); // just print summary for error case. report(true, output, MemReporterBase::default_scale); // just print summary for error case.
output->print("Preinit state:"); output->print("Preinit state:");
NMTPreInit::print_state(output); NMTPreInit::print_state(output);
MallocMemorySummary::print_limits(output);
} }
} }
@ -154,5 +160,6 @@ void MemTracker::tuning_statistics(outputStream* out) {
out->cr(); out->cr();
out->print_cr("Preinit state:"); out->print_cr("Preinit state:");
NMTPreInit::print_state(out); NMTPreInit::print_state(out);
MallocMemorySummary::print_limits(out);
out->cr(); out->cr();
} }

View file

@ -25,14 +25,14 @@
#include "services/nmtCommon.hpp" #include "services/nmtCommon.hpp"
#include "utilities/globalDefinitions.hpp" #include "utilities/globalDefinitions.hpp"
#define MEMORY_TYPE_DECLARE_NAME(type, human_readable) \
human_readable,
STATIC_ASSERT(NMT_off > NMT_unknown); STATIC_ASSERT(NMT_off > NMT_unknown);
STATIC_ASSERT(NMT_summary > NMT_off); STATIC_ASSERT(NMT_summary > NMT_off);
STATIC_ASSERT(NMT_detail > NMT_summary); STATIC_ASSERT(NMT_detail > NMT_summary);
const char* NMTUtil::_memory_type_names[] = { #define MEMORY_TYPE_DECLARE_NAME(type, human_readable) \
{ #type, human_readable },
NMTUtil::S NMTUtil::_strings[] = {
MEMORY_TYPES_DO(MEMORY_TYPE_DECLARE_NAME) MEMORY_TYPES_DO(MEMORY_TYPE_DECLARE_NAME)
}; };
@ -86,3 +86,16 @@ NMT_TrackingLevel NMTUtil::parse_tracking_level(const char* s) {
} }
return NMT_unknown; return NMT_unknown;
} }
MEMFLAGS NMTUtil::string_to_flag(const char* s) {
for (int i = 0; i < mt_number_of_types; i ++) {
assert(::strlen(_strings[i].enum_s) > 2, "Sanity"); // should always start with "mt"
if (::strcasecmp(_strings[i].human_readable, s) == 0 ||
::strcasecmp(_strings[i].enum_s, s) == 0 ||
::strcasecmp(_strings[i].enum_s + 2, s) == 0) // "mtXXX" -> match also "XXX" or "xxx"
{
return (MEMFLAGS)i;
}
}
return mtNone;
}

View file

@ -93,7 +93,7 @@ class NMTUtil : AllStatic {
// Map memory type to human readable name // Map memory type to human readable name
static const char* flag_to_name(MEMFLAGS flag) { static const char* flag_to_name(MEMFLAGS flag) {
return _memory_type_names[flag_to_index(flag)]; return _strings[flag_to_index(flag)].human_readable;
} }
// Map an index to memory type // Map an index to memory type
@ -115,11 +115,20 @@ class NMTUtil : AllStatic {
// string is not a valid level. // string is not a valid level.
static NMT_TrackingLevel parse_tracking_level(const char* s); static NMT_TrackingLevel parse_tracking_level(const char* s);
// Given a string, return associated flag. mtNone if name is invalid.
// String can be either the human readable name or the
// stringified enum (with or without leading "mt". In all cases, case is ignored.
static MEMFLAGS string_to_flag(const char* name);
// Returns textual representation of a tracking level. // Returns textual representation of a tracking level.
static const char* tracking_level_to_string(NMT_TrackingLevel level); static const char* tracking_level_to_string(NMT_TrackingLevel level);
private: private:
static const char* _memory_type_names[mt_number_of_types]; struct S {
const char* enum_s; // e.g. "mtNMT"
const char* human_readable; // e.g. "Native Memory Tracking"
};
static S _strings[mt_number_of_types];
}; };

View file

@ -0,0 +1,248 @@
/*
* Copyright (c) 2022 SAP SE. All rights reserved.
* Copyright (c) 2022, 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.
*
*/
/*
* @test id=global-limit
* @summary Verify -XX:MallocLimit with a global limit
* @modules java.base/jdk.internal.misc
* @library /test/lib
* @run driver MallocLimitTest global-limit
*/
/*
* @test id=compiler-limit
* @summary Verify -XX:MallocLimit with a compiler-specific limit (for "mtCompiler" category)
* @modules java.base/jdk.internal.misc
* @library /test/lib
* @run driver MallocLimitTest compiler-limit
*/
/*
* @test id=multi-limit
* @summary Verify -XX:MallocLimit with multiple limits
* @modules java.base/jdk.internal.misc
* @library /test/lib
* @run driver MallocLimitTest multi-limit
*/
/*
* @test id=valid-settings
* @summary Verify -XX:MallocLimit rejects invalid settings
* @modules java.base/jdk.internal.misc
* @library /test/lib
* @run driver MallocLimitTest valid-settings
*/
/*
* @test id=invalid-settings
* @summary Verify -XX:MallocLimit rejects invalid settings
* @modules java.base/jdk.internal.misc
* @library /test/lib
* @run driver MallocLimitTest invalid-settings
*/
/*
* @test id=limit-without-nmt
* @summary Verify that the VM warns if -XX:MallocLimit is given but NMT is disabled
* @modules java.base/jdk.internal.misc
* @library /test/lib
* @run driver MallocLimitTest limit-without-nmt
*/
import jdk.test.lib.Asserts;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class MallocLimitTest {
private static ProcessBuilder processBuilderWithSetting(String... extraSettings) {
List<String> args = new ArrayList<>();
args.add("-XX:+UnlockDiagnosticVMOptions"); // MallocLimit is diagnostic
args.add("-Xmx64m");
args.add("-XX:-CreateCoredumpOnCrash");
args.add("-Xlog:nmt");
args.add("-XX:NativeMemoryTracking=summary");
args.addAll(Arrays.asList(extraSettings));
args.add("-version");
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(args);
return pb;
}
private static void testGlobalLimit() throws IOException {
long smallMemorySize = 1024*1024; // 1m
ProcessBuilder pb = processBuilderWithSetting("-XX:MallocLimit=" + smallMemorySize);
OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.shouldNotHaveExitValue(0);
output.shouldContain("[nmt] MallocLimit: total limit: 1024K"); // printed by byte_size_in_proper_unit()
String s = output.firstMatch(".*MallocLimit: reached limit \\(size: (\\d+), limit: " + smallMemorySize + "\\).*", 1);
Asserts.assertNotNull(s);
long size = Long.parseLong(s);
Asserts.assertGreaterThan(size, smallMemorySize);
}
private static void testCompilerLimit() throws IOException {
// Here, we count on the VM, running with -Xcomp and with 1m of arena space allowed, will start a compilation
// and then trip over the limit.
// If limit is too small, Compiler stops too early and we won't get a Retry file (see below, we check that).
// If limit is too large, we may not trigger it for java -version.
// 1m seems to work out fine.
long smallMemorySize = 1024*1024; // 1m
ProcessBuilder pb = processBuilderWithSetting("-XX:MallocLimit=compiler:" + smallMemorySize,
"-Xcomp" // make sure we hit the compiler category limit
);
OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.shouldNotHaveExitValue(0);
output.shouldContain("[nmt] MallocLimit: category \"Compiler\" limit: 1024K"); // printed by byte_size_in_proper_unit
String s = output.firstMatch(".*MallocLimit: category \"Compiler\" reached limit \\(size: (\\d+), limit: " + smallMemorySize + "\\).*", 1);
Asserts.assertNotNull(s);
long size = Long.parseLong(s);
output.shouldContain("Compiler replay data is saved as");
Asserts.assertGreaterThan(size, smallMemorySize);
}
private static void testMultiLimit() throws IOException {
long smallMemorySize = 1024; // 1k
ProcessBuilder pb = processBuilderWithSetting("-XX:MallocLimit=mtOther:2g,compiler:1g,internal:" + smallMemorySize);
OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.shouldNotHaveExitValue(0);
output.shouldContain("[nmt] MallocLimit: category \"Compiler\" limit: 1024M");
output.shouldContain("[nmt] MallocLimit: category \"Internal\" limit: 1024B");
output.shouldContain("[nmt] MallocLimit: category \"Other\" limit: 2048M");
String s = output.firstMatch(".*MallocLimit: category \"Internal\" reached limit \\(size: (\\d+), limit: " + smallMemorySize + "\\).*", 1);
long size = Long.parseLong(s);
Asserts.assertGreaterThan(size, smallMemorySize);
}
private static void testValidSetting(String setting, String... expected_output) throws IOException {
ProcessBuilder pb = processBuilderWithSetting("-XX:MallocLimit=" + setting);
OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.shouldHaveExitValue(0);
for (String expected : expected_output) {
output.shouldContain(expected);
}
}
private static void testValidSettings() throws IOException {
// Test a number of valid settings.
testValidSetting(
"2097152k",
"[nmt] MallocLimit: total limit: 2048M",
"[nmt] NMT initialized: summary"
);
testValidSetting(
"gc:1234567891,mtInternal:987654321,Object Monitors:1g",
"[nmt] MallocLimit: category \"GC\" limit: 1177M",
"[nmt] MallocLimit: category \"Internal\" limit: 941M",
"[nmt] MallocLimit: category \"Object Monitors\" limit: 1024M",
"[nmt] NMT initialized: summary"
);
// Set all categories individually:
testValidSetting(
"JavaHeap:1024m,Class:1025m,Thread:1026m,ThreadStack:1027m,Code:1028m,GC:1029m,GCCardSet:1030m,Compiler:1031m,JVMCI:1032m," +
"Internal:1033m,Other:1034m,Symbol:1035m,NMT:1036m,ClassShared:1037m,Chunk:1038m,Test:1039m,Tracing:1040m,Logging:1041m," +
"Statistics:1042m,Arguments:1043m,Module:1044m,Safepoint:1045m,Synchronizer:1046m,Serviceability:1047m,Metaspace:1048m,StringDedup:1049m,ObjectMonitor:1050m",
"[nmt] MallocLimit: category \"Java Heap\" limit: 1024M",
"[nmt] MallocLimit: category \"Class\" limit: 1025M",
"[nmt] MallocLimit: category \"Thread\" limit: 1026M",
"[nmt] MallocLimit: category \"Thread Stack\" limit: 1027M",
"[nmt] MallocLimit: category \"Code\" limit: 1028M",
"[nmt] MallocLimit: category \"GC\" limit: 1029M",
"[nmt] MallocLimit: category \"GCCardSet\" limit: 1030M",
"[nmt] MallocLimit: category \"Compiler\" limit: 1031M",
"[nmt] MallocLimit: category \"JVMCI\" limit: 1032M",
"[nmt] MallocLimit: category \"Internal\" limit: 1033M",
"[nmt] MallocLimit: category \"Other\" limit: 1034M",
"[nmt] MallocLimit: category \"Symbol\" limit: 1035M",
"[nmt] MallocLimit: category \"Native Memory Tracking\" limit: 1036M",
"[nmt] MallocLimit: category \"Shared class space\" limit: 1037M",
"[nmt] MallocLimit: category \"Arena Chunk\" limit: 1038M",
"[nmt] MallocLimit: category \"Test\" limit: 1039M",
"[nmt] MallocLimit: category \"Tracing\" limit: 1040M",
"[nmt] MallocLimit: category \"Logging\" limit: 1041M",
"[nmt] MallocLimit: category \"Statistics\" limit: 1042M",
"[nmt] MallocLimit: category \"Arguments\" limit: 1043M",
"[nmt] MallocLimit: category \"Module\" limit: 1044M",
"[nmt] MallocLimit: category \"Safepoint\" limit: 1045M",
"[nmt] MallocLimit: category \"Synchronization\" limit: 1046M",
"[nmt] MallocLimit: category \"Serviceability\" limit: 1047M",
"[nmt] MallocLimit: category \"Metaspace\" limit: 1048M",
"[nmt] MallocLimit: category \"String Deduplication\" limit: 1049M",
"[nmt] MallocLimit: category \"Object Monitors\" limit: 1050M",
"[nmt] NMT initialized: summary"
);
}
private static void testInvalidSetting(String setting, String expected_error) throws IOException {
ProcessBuilder pb = processBuilderWithSetting("-XX:MallocLimit=" + setting);
OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.reportDiagnosticSummary();
output.shouldNotHaveExitValue(0);
output.shouldContain(expected_error);
}
private static void testInvalidSettings() throws IOException {
// Test a number of invalid settings the parser should catch. VM should abort in initialization.
testInvalidSetting("gc", "MallocLimit: colon missing: gc");
testInvalidSetting("gc:abc", "Invalid MallocLimit size: abc");
testInvalidSetting("abcd:10m", "MallocLimit: invalid nmt category: abcd");
testInvalidSetting("nmt:100m,abcd:10m", "MallocLimit: invalid nmt category: abcd");
testInvalidSetting("0", "MallocLimit: limit must be > 0");
testInvalidSetting("GC:0", "MallocLimit: limit must be > 0");
}
private static void testLimitWithoutNmt() throws IOException {
ProcessBuilder pb = processBuilderWithSetting("-XX:NativeMemoryTracking=off", // overrides "summary" from processBuilderWithSetting()
"-XX:MallocLimit=3g");
OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.reportDiagnosticSummary();
output.shouldHaveExitValue(0); // Not a fatal error, just a warning
output.shouldContain("MallocLimit will be ignored since NMT is disabled");
}
public static void main(String args[]) throws Exception {
if (args[0].equals("global-limit")) {
testGlobalLimit();
} else if (args[0].equals("compiler-limit")) {
testCompilerLimit();
} else if (args[0].equals("multi-limit")) {
testMultiLimit();
} else if (args[0].equals("valid-settings")) {
testValidSettings();
} else if (args[0].equals("invalid-settings")) {
testInvalidSettings();
} else if (args[0].equals("limit-without-nmt")) {
testLimitWithoutNmt();
} else {
throw new RuntimeException("invalid test: " + args[0]);
}
}
}