Skip to content

Commit 90e0922

Browse files
committed
8293313: NMT: Rework MallocLimit
8293292: Remove MallocMaxTestWords Reviewed-by: jsjolen, gziemski, lucy, mbaesken
1 parent f558a6c commit 90e0922

21 files changed

+753
-357
lines changed

src/hotspot/share/memory/arena.cpp

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*
22
* Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2019, 2023 SAP SE. All rights reserved.
34
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
45
*
56
* This code is free software; you can redistribute it and/or modify it
@@ -29,7 +30,7 @@
2930
#include "runtime/os.hpp"
3031
#include "runtime/task.hpp"
3132
#include "runtime/threadCritical.hpp"
32-
#include "services/memTracker.hpp"
33+
#include "services/memTracker.inline.hpp"
3334
#include "utilities/align.hpp"
3435
#include "utilities/debug.hpp"
3536
#include "utilities/ostream.hpp"
@@ -309,6 +310,10 @@ void* Arena::grow(size_t x, AllocFailType alloc_failmode) {
309310
// (Note: all chunk sizes have to be 64-bit aligned)
310311
size_t len = MAX2(ARENA_ALIGN(x), (size_t) Chunk::size);
311312

313+
if (MemTracker::check_exceeds_limit(x, _flags)) {
314+
return nullptr;
315+
}
316+
312317
Chunk *k = _chunk; // Get filled-up chunk address
313318
_chunk = new (alloc_failmode, len) Chunk(len);
314319

src/hotspot/share/runtime/arguments.cpp

-75
Original file line numberDiff line numberDiff line change
@@ -4263,78 +4263,3 @@ bool Arguments::copy_expand_pid(const char* src, size_t srclen,
42634263
*b = '\0';
42644264
return (p == src_end); // return false if not all of the source was copied
42654265
}
4266-
4267-
bool Arguments::parse_malloc_limit_size(const char* s, size_t* out) {
4268-
julong limit = 0;
4269-
Arguments::ArgsRange range = parse_memory_size(s, &limit, 1, SIZE_MAX);
4270-
switch (range) {
4271-
case ArgsRange::arg_in_range:
4272-
*out = (size_t)limit;
4273-
return true;
4274-
case ArgsRange::arg_too_big: // only possible on 32-bit
4275-
vm_exit_during_initialization("MallocLimit: too large", s);
4276-
break;
4277-
case ArgsRange::arg_too_small:
4278-
vm_exit_during_initialization("MallocLimit: limit must be > 0");
4279-
break;
4280-
default:
4281-
break;
4282-
}
4283-
return false;
4284-
}
4285-
4286-
// Helper for parse_malloc_limits
4287-
void Arguments::parse_single_category_limit(char* expression, size_t limits[mt_number_of_types]) {
4288-
// <category>:<limit>
4289-
char* colon = ::strchr(expression, ':');
4290-
if (colon == nullptr) {
4291-
vm_exit_during_initialization("MallocLimit: colon missing", expression);
4292-
}
4293-
*colon = '\0';
4294-
MEMFLAGS f = NMTUtil::string_to_flag(expression);
4295-
if (f == mtNone) {
4296-
vm_exit_during_initialization("MallocLimit: invalid nmt category", expression);
4297-
}
4298-
if (parse_malloc_limit_size(colon + 1, limits + (int)f) == false) {
4299-
vm_exit_during_initialization("Invalid MallocLimit size", colon + 1);
4300-
}
4301-
}
4302-
4303-
void Arguments::parse_malloc_limits(size_t* total_limit, size_t limits[mt_number_of_types]) {
4304-
4305-
// Reset output to 0
4306-
*total_limit = 0;
4307-
for (int i = 0; i < mt_number_of_types; i ++) {
4308-
limits[i] = 0;
4309-
}
4310-
4311-
// We are done if the option is not given.
4312-
if (MallocLimit == nullptr) {
4313-
return;
4314-
}
4315-
4316-
// Global form?
4317-
if (parse_malloc_limit_size(MallocLimit, total_limit)) {
4318-
return;
4319-
}
4320-
4321-
// No. So it must be in category-specific form: MallocLimit=<nmt category>:<size>[,<nmt category>:<size> ..]
4322-
char* copy = os::strdup(MallocLimit);
4323-
if (copy == nullptr) {
4324-
vm_exit_out_of_memory(strlen(MallocLimit), OOM_MALLOC_ERROR, "MallocLimit");
4325-
}
4326-
4327-
char* p = copy, *q;
4328-
do {
4329-
q = p;
4330-
p = ::strchr(q, ',');
4331-
if (p != nullptr) {
4332-
*p = '\0';
4333-
p ++;
4334-
}
4335-
parse_single_category_limit(q, limits);
4336-
} while (p != nullptr);
4337-
4338-
os::free(copy);
4339-
4340-
}

src/hotspot/share/runtime/arguments.hpp

-14
Original file line numberDiff line numberDiff line change
@@ -477,10 +477,6 @@ class Arguments : AllStatic {
477477
char** base_archive_path,
478478
char** top_archive_path) NOT_CDS_RETURN;
479479

480-
// Helpers for parse_malloc_limits
481-
static bool parse_malloc_limit_size(const char* s, size_t* out);
482-
static void parse_single_category_limit(char* expression, size_t limits[mt_number_of_types]);
483-
484480
public:
485481
static int num_archives(const char* archive_path) NOT_CDS_RETURN_(0);
486482
// Parses the arguments, first phase
@@ -651,16 +647,6 @@ class Arguments : AllStatic {
651647
assert(Arguments::is_dumping_archive(), "dump time only");
652648
}
653649

654-
// Parse diagnostic NMT switch "MallocLimit" and return the found limits.
655-
// 1) If option is not given, it will set all limits to 0 (aka "no limit").
656-
// 2) If option is given in the global form (-XX:MallocLimit=<size>), it
657-
// will return the size in *total_limit.
658-
// 3) If option is given in its per-NMT-category form (-XX:MallocLimit=<category>:<size>[,<category>:<size>]),
659-
// it will return all found limits in the limits array.
660-
// 4) If option is malformed, it will exit the VM.
661-
// For (2) and (3), limits not affected by the switch will be set to 0.
662-
static void parse_malloc_limits(size_t* total_limit, size_t limits[mt_number_of_types]);
663-
664650
DEBUG_ONLY(static bool verify_special_jvm_flags(bool check_globals);)
665651
};
666652

src/hotspot/share/runtime/globals.hpp

+13-11
Original file line numberDiff line numberDiff line change
@@ -1341,20 +1341,22 @@ const int ObjectAlignmentInBytes = 8;
13411341
notproduct(intx, ZombieALotInterval, 5, \
13421342
"Number of exits until ZombieALot kicks in") \
13431343
\
1344-
product(uintx, MallocMaxTestWords, 0, DIAGNOSTIC, \
1345-
"If non-zero, maximum number of words that malloc/realloc can " \
1346-
"allocate (for testing only)") \
1347-
range(0, max_uintx) \
1348-
\
13491344
product(ccstr, MallocLimit, nullptr, DIAGNOSTIC, \
1350-
"Limit malloc allocation size from VM. Reaching the limit will " \
1351-
"trigger a fatal error. This feature requires " \
1345+
"Limit malloc allocation size from VM. Reaching a limit will " \
1346+
"trigger an action (see flag). This feature requires " \
13521347
"NativeMemoryTracking=summary or NativeMemoryTracking=detail." \
13531348
"Usage:" \
1354-
"- MallocLimit=<size> to set a total limit. " \
1355-
"- MallocLimit=<NMT category>:<size>[,<NMT category>:<size>...] " \
1356-
" to set one or more category-specific limits." \
1357-
"Example: -XX:MallocLimit=compiler:500m") \
1349+
"\"-XX:MallocLimit=<size>[:<flag>]\" sets a total limit." \
1350+
"\"-XX:MallocLimit=<category>:<size>[:<flag>][,<category>:<size>[:<flag>] ...]\"" \
1351+
"sets one or more category-specific limits." \
1352+
"<flag> defines the action upon reaching the limit:" \
1353+
"\"fatal\": end VM with a fatal error at the allocation site" \
1354+
"\"oom\" : will mimic a native OOM" \
1355+
"If <flag> is omitted, \"fatal\" is the default." \
1356+
"Examples:\n" \
1357+
"-XX:MallocLimit=2g" \
1358+
"-XX:MallocLimit=2g:oom" \
1359+
"-XX:MallocLimit=compiler:200m:oom,code:100m") \
13581360
\
13591361
product(intx, TypeProfileWidth, 2, \
13601362
"Number of receiver types to record in call/cast profile") \

src/hotspot/share/runtime/os.cpp

+12-28
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
#include "services/attachListener.hpp"
6565
#include "services/mallocTracker.hpp"
6666
#include "services/mallocHeader.inline.hpp"
67-
#include "services/memTracker.hpp"
67+
#include "services/memTracker.inline.hpp"
6868
#include "services/nmtPreInit.hpp"
6969
#include "services/nmtCommon.hpp"
7070
#include "services/threadService.hpp"
@@ -87,8 +87,6 @@ int os::_processor_count = 0;
8787
int os::_initial_active_processor_count = 0;
8888
os::PageSizes os::_page_sizes;
8989

90-
static size_t cur_malloc_words = 0; // current size for MallocMaxTestWords
91-
9290
DEBUG_ONLY(bool os::_mutex_init_done = false;)
9391

9492
int os::snprintf(char* buf, size_t len, const char* fmt, ...) {
@@ -607,23 +605,6 @@ char* os::strdup_check_oom(const char* str, MEMFLAGS flags) {
607605
return p;
608606
}
609607

610-
//
611-
// This function supports testing of the malloc out of memory
612-
// condition without really running the system out of memory.
613-
//
614-
615-
static bool has_reached_max_malloc_test_peak(size_t alloc_size) {
616-
if (MallocMaxTestWords > 0) {
617-
size_t words = (alloc_size / BytesPerWord);
618-
619-
if ((cur_malloc_words + words) > MallocMaxTestWords) {
620-
return true;
621-
}
622-
Atomic::add(&cur_malloc_words, words);
623-
}
624-
return false;
625-
}
626-
627608
#ifdef ASSERT
628609
static void check_crash_protection() {
629610
assert(!ThreadCrashProtection::is_crash_protected(Thread::current_or_null()),
@@ -658,8 +639,8 @@ void* os::malloc(size_t size, MEMFLAGS memflags, const NativeCallStack& stack) {
658639
// we chose the latter.
659640
size = MAX2((size_t)1, size);
660641

661-
// For the test flag -XX:MallocMaxTestWords
662-
if (has_reached_max_malloc_test_peak(size)) {
642+
// Observe MallocLimit
643+
if (MemTracker::check_exceeds_limit(size, memflags)) {
663644
return nullptr;
664645
}
665646

@@ -710,11 +691,6 @@ void* os::realloc(void *memblock, size_t size, MEMFLAGS memflags, const NativeCa
710691
// we chose the latter.
711692
size = MAX2((size_t)1, size);
712693

713-
// For the test flag -XX:MallocMaxTestWords
714-
if (has_reached_max_malloc_test_peak(size)) {
715-
return nullptr;
716-
}
717-
718694
if (MemTracker::enabled()) {
719695
// NMT realloc handling
720696

@@ -725,12 +701,20 @@ void* os::realloc(void *memblock, size_t size, MEMFLAGS memflags, const NativeCa
725701
return nullptr;
726702
}
727703

704+
const size_t old_size = MallocTracker::malloc_header(memblock)->size();
705+
706+
// Observe MallocLimit
707+
if ((size > old_size) && MemTracker::check_exceeds_limit(size - old_size, memflags)) {
708+
return nullptr;
709+
}
710+
728711
// Perform integrity checks on and mark the old block as dead *before* calling the real realloc(3) since it
729712
// may invalidate the old block, including its header.
730713
MallocHeader* header = MallocHeader::resolve_checked(memblock);
731714
assert(memflags == header->flags(), "weird NMT flags mismatch (new:\"%s\" != old:\"%s\")\n",
732715
NMTUtil::flag_to_name(memflags), NMTUtil::flag_to_name(header->flags()));
733716
const MallocHeader::FreeInfo free_info = header->free_info();
717+
734718
header->mark_block_as_dead();
735719

736720
// the real realloc
@@ -750,7 +734,7 @@ void* os::realloc(void *memblock, size_t size, MEMFLAGS memflags, const NativeCa
750734
void* const new_inner_ptr = MemTracker::record_malloc(new_outer_ptr, size, memflags, stack);
751735

752736
#ifdef ASSERT
753-
size_t old_size = free_info.size;
737+
assert(old_size == free_info.size, "Sanity");
754738
if (old_size < size) {
755739
// We also zap the newly extended region.
756740
::memset((char*)new_inner_ptr + old_size, uninitBlockPad, size - old_size);

0 commit comments

Comments
 (0)