8382616: Create GCArguments::set_heap_size interface

Reviewed-by: jsikstro, ayang
This commit is contained in:
Axel Boldt-Christmas 2026-04-27 19:40:13 +00:00
parent 64779a2541
commit 02c1fdeaef
5 changed files with 142 additions and 141 deletions

View File

@ -25,10 +25,12 @@
#include "gc/shared/cardTable.hpp"
#include "gc/shared/gcArguments.hpp"
#include "gc/shared/genArguments.hpp"
#include "logging/log.hpp"
#include "runtime/arguments.hpp"
#include "runtime/globals.hpp"
#include "runtime/globals_extension.hpp"
#include "runtime/os.hpp"
#include "utilities/formatBuffer.hpp"
#include "utilities/macros.hpp"
@ -56,6 +58,141 @@ void GCArguments::initialize() {
}
}
size_t GCArguments::limit_heap_by_allocatable_memory(size_t limit) {
// Limits the given heap size by the maximum amount of virtual
// memory this process is currently allowed to use. It also takes
// the virtual-to-physical ratio of the current GC into account.
size_t fraction = MaxVirtMemFraction * heap_virtual_to_physical_ratio();
size_t max_allocatable = os::commit_memory_limit();
return MIN2(limit, max_allocatable / fraction);
}
// Use static initialization to get the default before parsing
static const size_t DefaultHeapBaseMinAddress = HeapBaseMinAddress;
static size_t clamp_by_size_t_max(uint64_t value) {
return (size_t)MIN2(value, (uint64_t)std::numeric_limits<size_t>::max());
}
void GCArguments::set_heap_size() {
// Check if the user has configured any limit on the amount of RAM we may use.
bool has_ram_limit = !FLAG_IS_DEFAULT(MaxRAMPercentage) ||
!FLAG_IS_DEFAULT(MinRAMPercentage) ||
!FLAG_IS_DEFAULT(InitialRAMPercentage);
const physical_memory_size_type avail_mem = os::physical_memory();
// If the maximum heap size has not been set with -Xmx, then set it as
// fraction of the size of physical memory, respecting the maximum and
// minimum sizes of the heap.
if (FLAG_IS_DEFAULT(MaxHeapSize)) {
uint64_t min_memory = (uint64_t)(((double)avail_mem * MinRAMPercentage) / 100);
uint64_t max_memory = (uint64_t)(((double)avail_mem * MaxRAMPercentage) / 100);
const size_t reasonable_min = clamp_by_size_t_max(min_memory);
size_t reasonable_max = clamp_by_size_t_max(max_memory);
if (reasonable_min < MaxHeapSize) {
// Small physical memory, so use a minimum fraction of it for the heap
reasonable_max = reasonable_min;
} else {
// Not-small physical memory, so require a heap at least
// as large as MaxHeapSize
reasonable_max = MAX2(reasonable_max, MaxHeapSize);
}
if (!FLAG_IS_DEFAULT(ErgoHeapSizeLimit) && ErgoHeapSizeLimit != 0) {
// Limit the heap size to ErgoHeapSizeLimit
reasonable_max = MIN2(reasonable_max, ErgoHeapSizeLimit);
}
reasonable_max = limit_heap_by_allocatable_memory(reasonable_max);
if (!FLAG_IS_DEFAULT(InitialHeapSize)) {
// An initial heap size was specified on the command line,
// so be sure that the maximum size is consistent. Done
// after call to limit_heap_by_allocatable_memory because that
// method might reduce the allocation size.
reasonable_max = MAX2(reasonable_max, InitialHeapSize);
} else if (!FLAG_IS_DEFAULT(MinHeapSize)) {
reasonable_max = MAX2(reasonable_max, MinHeapSize);
}
#ifdef _LP64
if (UseCompressedOops) {
// HeapBaseMinAddress can be greater than default but not less than.
if (!FLAG_IS_DEFAULT(HeapBaseMinAddress)) {
if (HeapBaseMinAddress < DefaultHeapBaseMinAddress) {
// matches compressed oops printing flags
log_debug(gc, heap, coops)("HeapBaseMinAddress must be at least %zu "
"(%zuG) which is greater than value given %zu",
DefaultHeapBaseMinAddress,
DefaultHeapBaseMinAddress/G,
HeapBaseMinAddress);
FLAG_SET_ERGO(HeapBaseMinAddress, DefaultHeapBaseMinAddress);
}
}
uintptr_t heap_end = HeapBaseMinAddress + MaxHeapSize;
uintptr_t max_coop_heap = Arguments::max_heap_for_compressed_oops();
// Limit the heap size to the maximum possible when using compressed oops
if (heap_end < max_coop_heap) {
// Heap should be above HeapBaseMinAddress to get zero based compressed
// oops but it should be not less than default MaxHeapSize.
max_coop_heap -= HeapBaseMinAddress;
}
// If the user has configured any limit on the amount of RAM we may use,
// then disable compressed oops if the calculated max exceeds max_coop_heap
// and UseCompressedOops was not specified.
if (reasonable_max > max_coop_heap) {
if (FLAG_IS_ERGO(UseCompressedOops) && has_ram_limit) {
log_debug(gc, heap, coops)("UseCompressedOops disabled due to "
"max heap %zu > compressed oop heap %zu. "
"Please check the setting of MaxRAMPercentage %5.2f.",
reasonable_max, (size_t)max_coop_heap, MaxRAMPercentage);
FLAG_SET_ERGO(UseCompressedOops, false);
} else {
reasonable_max = max_coop_heap;
}
}
}
#endif // _LP64
log_trace(gc, heap)(" Maximum heap size %zu", reasonable_max);
FLAG_SET_ERGO(MaxHeapSize, reasonable_max);
}
// If the minimum or initial heap_size have not been set or requested to be set
// ergonomically, set them accordingly.
if (InitialHeapSize == 0 || MinHeapSize == 0) {
size_t reasonable_minimum = clamp_by_size_t_max((uint64_t)OldSize + (uint64_t)NewSize);
reasonable_minimum = MIN2(reasonable_minimum, MaxHeapSize);
reasonable_minimum = limit_heap_by_allocatable_memory(reasonable_minimum);
if (InitialHeapSize == 0) {
uint64_t initial_memory = (uint64_t)(((double)avail_mem * InitialRAMPercentage) / 100);
size_t reasonable_initial = clamp_by_size_t_max(initial_memory);
reasonable_initial = limit_heap_by_allocatable_memory(reasonable_initial);
reasonable_initial = MAX3(reasonable_initial, reasonable_minimum, MinHeapSize);
reasonable_initial = MIN2(reasonable_initial, MaxHeapSize);
FLAG_SET_ERGO(InitialHeapSize, (size_t)reasonable_initial);
log_trace(gc, heap)(" Initial heap size %zu", InitialHeapSize);
}
// If the minimum heap size has not been set (via -Xms or -XX:MinHeapSize),
// synchronize with InitialHeapSize to avoid errors with the default value.
if (MinHeapSize == 0) {
FLAG_SET_ERGO(MinHeapSize, MIN2(reasonable_minimum, InitialHeapSize));
log_trace(gc, heap)(" Minimum heap size %zu", MinHeapSize);
}
}
}
void GCArguments::initialize_heap_sizes() {
initialize_alignments();
initialize_heap_flags_and_sizes();

View File

@ -40,10 +40,13 @@ protected:
virtual void initialize_heap_flags_and_sizes();
virtual void initialize_size_info();
size_t limit_heap_by_allocatable_memory(size_t size);
DEBUG_ONLY(void assert_flags();)
DEBUG_ONLY(void assert_size_info();)
public:
virtual void set_heap_size();
virtual void initialize();
// Return the (conservative) maximum heap alignment

View File

@ -283,7 +283,7 @@ JVMFlag::Error SoftMaxHeapSizeConstraintFunc(size_t value, bool verbose) {
}
JVMFlag::Error HeapBaseMinAddressConstraintFunc(size_t value, bool verbose) {
// If an overflow happened in Arguments::set_heap_size(), MaxHeapSize will have too large a value.
// If an overflow happened in GCArguments::set_heap_size(), MaxHeapSize will have too large a value.
// Check for this by ensuring that MaxHeapSize plus the requested min base address still fit within max_uintx.
if (value > (max_uintx - MaxHeapSize)) {
JVMFlag::printError(verbose,

View File

@ -35,7 +35,6 @@
#include "gc/shared/gc_globals.hpp"
#include "gc/shared/gcArguments.hpp"
#include "gc/shared/gcConfig.hpp"
#include "gc/shared/genArguments.hpp"
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "gc/shared/tlab_globals.hpp"
#include "jvm.h"
@ -1489,138 +1488,6 @@ jint Arguments::set_ergonomics_flags() {
return JNI_OK;
}
size_t Arguments::limit_heap_by_allocatable_memory(size_t limit) {
size_t fraction = MaxVirtMemFraction * GCConfig::arguments()->heap_virtual_to_physical_ratio();
size_t max_allocatable = os::commit_memory_limit();
return MIN2(limit, max_allocatable / fraction);
}
// Use static initialization to get the default before parsing
static const size_t DefaultHeapBaseMinAddress = HeapBaseMinAddress;
static size_t clamp_by_size_t_max(uint64_t value) {
return (size_t)MIN2(value, (uint64_t)std::numeric_limits<size_t>::max());
}
void Arguments::set_heap_size() {
// Check if the user has configured any limit on the amount of RAM we may use.
bool has_ram_limit = !FLAG_IS_DEFAULT(MaxRAMPercentage) ||
!FLAG_IS_DEFAULT(MinRAMPercentage) ||
!FLAG_IS_DEFAULT(InitialRAMPercentage);
const physical_memory_size_type avail_mem = os::physical_memory();
// If the maximum heap size has not been set with -Xmx, then set it as
// fraction of the size of physical memory, respecting the maximum and
// minimum sizes of the heap.
if (FLAG_IS_DEFAULT(MaxHeapSize)) {
uint64_t min_memory = (uint64_t)(((double)avail_mem * MinRAMPercentage) / 100);
uint64_t max_memory = (uint64_t)(((double)avail_mem * MaxRAMPercentage) / 100);
const size_t reasonable_min = clamp_by_size_t_max(min_memory);
size_t reasonable_max = clamp_by_size_t_max(max_memory);
if (reasonable_min < MaxHeapSize) {
// Small physical memory, so use a minimum fraction of it for the heap
reasonable_max = reasonable_min;
} else {
// Not-small physical memory, so require a heap at least
// as large as MaxHeapSize
reasonable_max = MAX2(reasonable_max, MaxHeapSize);
}
if (!FLAG_IS_DEFAULT(ErgoHeapSizeLimit) && ErgoHeapSizeLimit != 0) {
// Limit the heap size to ErgoHeapSizeLimit
reasonable_max = MIN2(reasonable_max, ErgoHeapSizeLimit);
}
reasonable_max = limit_heap_by_allocatable_memory(reasonable_max);
if (!FLAG_IS_DEFAULT(InitialHeapSize)) {
// An initial heap size was specified on the command line,
// so be sure that the maximum size is consistent. Done
// after call to limit_heap_by_allocatable_memory because that
// method might reduce the allocation size.
reasonable_max = MAX2(reasonable_max, InitialHeapSize);
} else if (!FLAG_IS_DEFAULT(MinHeapSize)) {
reasonable_max = MAX2(reasonable_max, MinHeapSize);
}
#ifdef _LP64
if (UseCompressedOops) {
// HeapBaseMinAddress can be greater than default but not less than.
if (!FLAG_IS_DEFAULT(HeapBaseMinAddress)) {
if (HeapBaseMinAddress < DefaultHeapBaseMinAddress) {
// matches compressed oops printing flags
log_debug(gc, heap, coops)("HeapBaseMinAddress must be at least %zu "
"(%zuG) which is greater than value given %zu",
DefaultHeapBaseMinAddress,
DefaultHeapBaseMinAddress/G,
HeapBaseMinAddress);
FLAG_SET_ERGO(HeapBaseMinAddress, DefaultHeapBaseMinAddress);
}
}
uintptr_t heap_end = HeapBaseMinAddress + MaxHeapSize;
uintptr_t max_coop_heap = max_heap_for_compressed_oops();
// Limit the heap size to the maximum possible when using compressed oops
if (heap_end < max_coop_heap) {
// Heap should be above HeapBaseMinAddress to get zero based compressed
// oops but it should be not less than default MaxHeapSize.
max_coop_heap -= HeapBaseMinAddress;
}
// If the user has configured any limit on the amount of RAM we may use,
// then disable compressed oops if the calculated max exceeds max_coop_heap
// and UseCompressedOops was not specified.
if (reasonable_max > max_coop_heap) {
if (FLAG_IS_ERGO(UseCompressedOops) && has_ram_limit) {
log_debug(gc, heap, coops)("UseCompressedOops disabled due to "
"max heap %zu > compressed oop heap %zu. "
"Please check the setting of MaxRAMPercentage %5.2f.",
reasonable_max, (size_t)max_coop_heap, MaxRAMPercentage);
FLAG_SET_ERGO(UseCompressedOops, false);
} else {
reasonable_max = max_coop_heap;
}
}
}
#endif // _LP64
log_trace(gc, heap)(" Maximum heap size %zu", reasonable_max);
FLAG_SET_ERGO(MaxHeapSize, reasonable_max);
}
// If the minimum or initial heap_size have not been set or requested to be set
// ergonomically, set them accordingly.
if (InitialHeapSize == 0 || MinHeapSize == 0) {
size_t reasonable_minimum = clamp_by_size_t_max((uint64_t)OldSize + (uint64_t)NewSize);
reasonable_minimum = MIN2(reasonable_minimum, MaxHeapSize);
reasonable_minimum = limit_heap_by_allocatable_memory(reasonable_minimum);
if (InitialHeapSize == 0) {
uint64_t initial_memory = (uint64_t)(((double)avail_mem * InitialRAMPercentage) / 100);
size_t reasonable_initial = clamp_by_size_t_max(initial_memory);
reasonable_initial = limit_heap_by_allocatable_memory(reasonable_initial);
reasonable_initial = MAX3(reasonable_initial, reasonable_minimum, MinHeapSize);
reasonable_initial = MIN2(reasonable_initial, MaxHeapSize);
FLAG_SET_ERGO(InitialHeapSize, (size_t)reasonable_initial);
log_trace(gc, heap)(" Initial heap size %zu", InitialHeapSize);
}
// If the minimum heap size has not been set (via -Xms or -XX:MinHeapSize),
// synchronize with InitialHeapSize to avoid errors with the default value.
if (MinHeapSize == 0) {
FLAG_SET_ERGO(MinHeapSize, MIN2(reasonable_minimum, InitialHeapSize));
log_trace(gc, heap)(" Minimum heap size %zu", MinHeapSize);
}
}
}
// This must be called after ergonomics.
void Arguments::set_bytecode_flags() {
if (!RewriteBytecodes) {
@ -3677,7 +3544,7 @@ jint Arguments::apply_ergo() {
if (result != JNI_OK) return result;
// Set heap size based on available physical memory
set_heap_size();
GCConfig::arguments()->set_heap_size();
GCConfig::arguments()->initialize();

View File

@ -273,12 +273,6 @@ class Arguments : AllStatic {
static void set_use_compressed_oops();
static jint set_ergonomics_flags();
static void set_compact_headers_flags();
// Limits the given heap size by the maximum amount of virtual
// memory this process is currently allowed to use. It also takes
// the virtual-to-physical ratio of the current GC into account.
static size_t limit_heap_by_allocatable_memory(size_t size);
// Setup heap size
static void set_heap_size();
// Bytecode rewriting
static void set_bytecode_flags();