mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-19 18:44:38 +02:00
8291878: NMT: Malloc limits
Reviewed-by: kvn, shade
This commit is contained in:
parent
ad2e0c4df0
commit
fa5cc4cc8e
9 changed files with 475 additions and 7 deletions
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -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);)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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) \
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
248
test/hotspot/jtreg/runtime/NMT/MallocLimitTest.java
Normal file
248
test/hotspot/jtreg/runtime/NMT/MallocLimitTest.java
Normal 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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue