From 4ac33956343bbfa3619ccb029ceed6c5a402f775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joel=20Sikstr=C3=B6m?= Date: Thu, 27 Nov 2025 09:38:59 +0000 Subject: [PATCH] 8372150: Parallel: Tighten requirements around heap sizes with NUMA and Large Pages Reviewed-by: ayang, stefank, aboldtch, stuefe --- .../share/gc/parallel/parallelArguments.cpp | 102 +++++++++++------- .../share/gc/parallel/parallelArguments.hpp | 10 +- .../share/gc/serial/serialArguments.cpp | 39 +++++++ .../share/gc/serial/serialArguments.hpp | 7 +- src/hotspot/share/gc/shared/gcArguments.cpp | 18 ---- src/hotspot/share/gc/shared/gcArguments.hpp | 4 +- src/hotspot/share/gc/shared/genArguments.cpp | 18 ---- src/hotspot/share/gc/shared/genArguments.hpp | 7 +- .../share/gc/shared/jvmFlagConstraintsGC.cpp | 2 +- src/hotspot/share/runtime/arguments.cpp | 5 +- .../gtest/gc/shared/test_collectorPolicy.cpp | 11 -- 11 files changed, 118 insertions(+), 105 deletions(-) diff --git a/src/hotspot/share/gc/parallel/parallelArguments.cpp b/src/hotspot/share/gc/parallel/parallelArguments.cpp index 629690a6258..be9673224f5 100644 --- a/src/hotspot/share/gc/parallel/parallelArguments.cpp +++ b/src/hotspot/share/gc/parallel/parallelArguments.cpp @@ -37,8 +37,45 @@ #include "utilities/defaultStream.hpp" #include "utilities/powerOfTwo.hpp" -size_t ParallelArguments::conservative_max_heap_alignment() { - return compute_heap_alignment(); +static size_t num_young_spaces() { + // When using NUMA, we create one MutableNUMASpace for each NUMA node + const size_t num_eden_spaces = UseNUMA ? os::numa_get_groups_num() : 1; + + // The young generation must have room for eden + two survivors + return num_eden_spaces + 2; +} + +static size_t num_old_spaces() { + return 1; +} + +void ParallelArguments::initialize_alignments() { + // Initialize card size before initializing alignments + CardTable::initialize_card_size(); + const size_t card_table_alignment = CardTable::ct_max_alignment_constraint(); + SpaceAlignment = ParallelScavengeHeap::default_space_alignment(); + + if (UseLargePages) { + const size_t total_spaces = num_young_spaces() + num_old_spaces(); + const size_t page_size = os::page_size_for_region_unaligned(MaxHeapSize, total_spaces); + ParallelScavengeHeap::set_desired_page_size(page_size); + + if (page_size == os::vm_page_size()) { + log_warning(gc, heap)("MaxHeapSize (%zu) must be large enough for %zu * page-size; Disabling UseLargePages for heap", + MaxHeapSize, total_spaces); + } + + if (page_size > SpaceAlignment) { + SpaceAlignment = page_size; + } + + HeapAlignment = lcm(page_size, card_table_alignment); + + } else { + assert(is_aligned(SpaceAlignment, os::vm_page_size()), ""); + ParallelScavengeHeap::set_desired_page_size(os::vm_page_size()); + HeapAlignment = card_table_alignment; + } } void ParallelArguments::initialize() { @@ -98,49 +135,36 @@ void ParallelArguments::initialize() { FullGCForwarding::initialize_flags(heap_reserved_size_bytes()); } -void ParallelArguments::initialize_alignments() { - // Initialize card size before initializing alignments - CardTable::initialize_card_size(); - SpaceAlignment = ParallelScavengeHeap::default_space_alignment(); - HeapAlignment = compute_heap_alignment(); -} +size_t ParallelArguments::conservative_max_heap_alignment() { + // The card marking array and the offset arrays for old generations are + // committed in os pages as well. Make sure they are entirely full (to + // avoid partial page problems), e.g. if 512 bytes heap corresponds to 1 + // byte entry and the os page size is 4096, the maximum heap size should + // be 512*4096 = 2MB aligned. -void ParallelArguments::initialize_heap_flags_and_sizes_one_pass() { - // Do basic sizing work - GenArguments::initialize_heap_flags_and_sizes(); -} + size_t alignment = CardTable::ct_max_alignment_constraint(); -void ParallelArguments::initialize_heap_flags_and_sizes() { - initialize_heap_flags_and_sizes_one_pass(); - - if (!UseLargePages) { - ParallelScavengeHeap::set_desired_page_size(os::vm_page_size()); - return; + if (UseLargePages) { + // In presence of large pages we have to make sure that our + // alignment is large page aware. + alignment = lcm(os::large_page_size(), alignment); } - // If using large-page, need to update SpaceAlignment so that spaces are page-size aligned. - const size_t min_pages = 4; // 1 for eden + 1 for each survivor + 1 for old - const size_t page_sz = os::page_size_for_region_aligned(MinHeapSize, min_pages); - ParallelScavengeHeap::set_desired_page_size(page_sz); - - if (page_sz == os::vm_page_size()) { - log_warning(gc, heap)("MinHeapSize (%zu) must be large enough for 4 * page-size; Disabling UseLargePages for heap", MinHeapSize); - return; - } - - // Space is largepage-aligned. - size_t new_alignment = page_sz; - if (new_alignment != SpaceAlignment) { - SpaceAlignment = new_alignment; - // Redo everything from the start - initialize_heap_flags_and_sizes_one_pass(); - } -} - -size_t ParallelArguments::heap_reserved_size_bytes() { - return MaxHeapSize; + return alignment; } CollectedHeap* ParallelArguments::create_heap() { return new ParallelScavengeHeap(); } + +size_t ParallelArguments::young_gen_size_lower_bound() { + return num_young_spaces() * SpaceAlignment; +} + +size_t ParallelArguments::old_gen_size_lower_bound() { + return num_old_spaces() * SpaceAlignment; +} + +size_t ParallelArguments::heap_reserved_size_bytes() { + return MaxHeapSize; +} diff --git a/src/hotspot/share/gc/parallel/parallelArguments.hpp b/src/hotspot/share/gc/parallel/parallelArguments.hpp index 159441be792..729fe43b879 100644 --- a/src/hotspot/share/gc/parallel/parallelArguments.hpp +++ b/src/hotspot/share/gc/parallel/parallelArguments.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, Red Hat, Inc. and/or its affiliates. + * Copyright (c) 2025, 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 @@ -25,21 +26,16 @@ #ifndef SHARE_GC_PARALLEL_PARALLELARGUMENTS_HPP #define SHARE_GC_PARALLEL_PARALLELARGUMENTS_HPP -#include "gc/shared/gcArguments.hpp" #include "gc/shared/genArguments.hpp" -class CollectedHeap; - class ParallelArguments : public GenArguments { private: virtual void initialize_alignments(); - virtual void initialize_heap_flags_and_sizes(); - - void initialize_heap_flags_and_sizes_one_pass(); - virtual void initialize(); virtual size_t conservative_max_heap_alignment(); virtual CollectedHeap* create_heap(); + virtual size_t young_gen_size_lower_bound(); + virtual size_t old_gen_size_lower_bound(); public: static size_t heap_reserved_size_bytes(); diff --git a/src/hotspot/share/gc/serial/serialArguments.cpp b/src/hotspot/share/gc/serial/serialArguments.cpp index aed1c2353b4..efebec4fa38 100644 --- a/src/hotspot/share/gc/serial/serialArguments.cpp +++ b/src/hotspot/share/gc/serial/serialArguments.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, Red Hat, Inc. and/or its affiliates. + * Copyright (c) 2025, 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 @@ -27,11 +28,49 @@ #include "gc/shared/fullGCForwarding.hpp" #include "gc/shared/gcArguments.hpp" +static size_t compute_heap_alignment() { + // The card marking array and the offset arrays for old generations are + // committed in os pages as well. Make sure they are entirely full (to + // avoid partial page problems), e.g. if 512 bytes heap corresponds to 1 + // byte entry and the os page size is 4096, the maximum heap size should + // be 512*4096 = 2MB aligned. + + size_t alignment = CardTable::ct_max_alignment_constraint(); + + if (UseLargePages) { + // In presence of large pages we have to make sure that our + // alignment is large page aware. + alignment = lcm(os::large_page_size(), alignment); + } + + return alignment; +} + +void SerialArguments::initialize_alignments() { + // Initialize card size before initializing alignments + CardTable::initialize_card_size(); + SpaceAlignment = (size_t)Generation::GenGrain; + HeapAlignment = compute_heap_alignment(); +} + void SerialArguments::initialize() { GCArguments::initialize(); FullGCForwarding::initialize_flags(MaxHeapSize); } +size_t SerialArguments::conservative_max_heap_alignment() { + return MAX2((size_t)Generation::GenGrain, compute_heap_alignment()); +} + CollectedHeap* SerialArguments::create_heap() { return new SerialHeap(); } + +size_t SerialArguments::young_gen_size_lower_bound() { + // The young generation must be aligned and have room for eden + two survivors + return 3 * SpaceAlignment; +} + +size_t SerialArguments::old_gen_size_lower_bound() { + return SpaceAlignment; +} diff --git a/src/hotspot/share/gc/serial/serialArguments.hpp b/src/hotspot/share/gc/serial/serialArguments.hpp index 90c3225ff8d..774168eb626 100644 --- a/src/hotspot/share/gc/serial/serialArguments.hpp +++ b/src/hotspot/share/gc/serial/serialArguments.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, Red Hat, Inc. and/or its affiliates. + * Copyright (c) 2025, 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 @@ -27,12 +28,14 @@ #include "gc/shared/genArguments.hpp" -class CollectedHeap; - class SerialArguments : public GenArguments { private: + virtual void initialize_alignments(); virtual void initialize(); + virtual size_t conservative_max_heap_alignment(); virtual CollectedHeap* create_heap(); + virtual size_t young_gen_size_lower_bound(); + virtual size_t old_gen_size_lower_bound(); }; #endif // SHARE_GC_SERIAL_SERIALARGUMENTS_HPP diff --git a/src/hotspot/share/gc/shared/gcArguments.cpp b/src/hotspot/share/gc/shared/gcArguments.cpp index d45e6a9c7dd..424427c12b6 100644 --- a/src/hotspot/share/gc/shared/gcArguments.cpp +++ b/src/hotspot/share/gc/shared/gcArguments.cpp @@ -62,24 +62,6 @@ void GCArguments::initialize_heap_sizes() { initialize_size_info(); } -size_t GCArguments::compute_heap_alignment() { - // The card marking array and the offset arrays for old generations are - // committed in os pages as well. Make sure they are entirely full (to - // avoid partial page problems), e.g. if 512 bytes heap corresponds to 1 - // byte entry and the os page size is 4096, the maximum heap size should - // be 512*4096 = 2MB aligned. - - size_t alignment = CardTable::ct_max_alignment_constraint(); - - if (UseLargePages) { - // In presence of large pages we have to make sure that our - // alignment is large page aware. - alignment = lcm(os::large_page_size(), alignment); - } - - return alignment; -} - #ifdef ASSERT void GCArguments::assert_flags() { assert(InitialHeapSize <= MaxHeapSize, "Ergonomics decided on incompatible initial and maximum heap sizes"); diff --git a/src/hotspot/share/gc/shared/gcArguments.hpp b/src/hotspot/share/gc/shared/gcArguments.hpp index fff41e85d8c..d8a4901f887 100644 --- a/src/hotspot/share/gc/shared/gcArguments.hpp +++ b/src/hotspot/share/gc/shared/gcArguments.hpp @@ -45,6 +45,8 @@ protected: public: virtual void initialize(); + + // Return the (conservative) maximum heap alignment virtual size_t conservative_max_heap_alignment() = 0; // Used by heap size heuristics to determine max @@ -59,8 +61,6 @@ public: } void initialize_heap_sizes(); - - static size_t compute_heap_alignment(); }; #endif // SHARE_GC_SHARED_GCARGUMENTS_HPP diff --git a/src/hotspot/share/gc/shared/genArguments.cpp b/src/hotspot/share/gc/shared/genArguments.cpp index 9618c515b7d..5d5003f8d9f 100644 --- a/src/hotspot/share/gc/shared/genArguments.cpp +++ b/src/hotspot/share/gc/shared/genArguments.cpp @@ -42,17 +42,6 @@ size_t MaxOldSize = 0; // See more in JDK-8346005 size_t OldSize = ScaleForWordSize(4*M); -size_t GenArguments::conservative_max_heap_alignment() { return (size_t)Generation::GenGrain; } - -static size_t young_gen_size_lower_bound() { - // The young generation must be aligned and have room for eden + two survivors - return 3 * SpaceAlignment; -} - -static size_t old_gen_size_lower_bound() { - return SpaceAlignment; -} - size_t GenArguments::scale_by_NewRatio_aligned(size_t base_size, size_t alignment) { return align_down_bounded(base_size / (NewRatio + 1), alignment); } @@ -64,13 +53,6 @@ static size_t bound_minus_alignment(size_t desired_size, return MIN2(desired_size, max_minus); } -void GenArguments::initialize_alignments() { - // Initialize card size before initializing alignments - CardTable::initialize_card_size(); - SpaceAlignment = (size_t)Generation::GenGrain; - HeapAlignment = compute_heap_alignment(); -} - void GenArguments::initialize_heap_flags_and_sizes() { GCArguments::initialize_heap_flags_and_sizes(); diff --git a/src/hotspot/share/gc/shared/genArguments.hpp b/src/hotspot/share/gc/shared/genArguments.hpp index 80133bd1ec1..0ff9568575d 100644 --- a/src/hotspot/share/gc/shared/genArguments.hpp +++ b/src/hotspot/share/gc/shared/genArguments.hpp @@ -38,17 +38,16 @@ extern size_t OldSize; class GenArguments : public GCArguments { friend class TestGenCollectorPolicy; // Testing private: - virtual void initialize_alignments(); virtual void initialize_size_info(); - // Return the (conservative) maximum heap alignment - virtual size_t conservative_max_heap_alignment(); - DEBUG_ONLY(void assert_flags();) DEBUG_ONLY(void assert_size_info();) static size_t scale_by_NewRatio_aligned(size_t base_size, size_t alignment); + virtual size_t young_gen_size_lower_bound() = 0; + virtual size_t old_gen_size_lower_bound() = 0; + protected: virtual void initialize_heap_flags_and_sizes(); }; diff --git a/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.cpp b/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.cpp index 1ed3701fdab..cc446adba66 100644 --- a/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.cpp +++ b/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.cpp @@ -250,7 +250,7 @@ static JVMFlag::Error MaxSizeForHeapAlignment(const char* name, size_t value, bo } else #endif { - heap_alignment = GCArguments::compute_heap_alignment(); + heap_alignment = Arguments::conservative_max_heap_alignment(); } return MaxSizeForAlignment(name, value, heap_alignment, verbose); diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index ff1de899bdd..01fee949e33 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -1478,10 +1478,9 @@ void Arguments::set_conservative_max_heap_alignment() { // the alignments imposed by several sources: any requirements from the heap // itself and the maximum page size we may run the VM with. size_t heap_alignment = GCConfig::arguments()->conservative_max_heap_alignment(); - _conservative_max_heap_alignment = MAX4(heap_alignment, + _conservative_max_heap_alignment = MAX3(heap_alignment, os::vm_allocation_granularity(), - os::max_page_size(), - GCArguments::compute_heap_alignment()); + os::max_page_size()); assert(is_power_of_2(_conservative_max_heap_alignment), "Expected to be a power-of-2"); } diff --git a/test/hotspot/gtest/gc/shared/test_collectorPolicy.cpp b/test/hotspot/gtest/gc/shared/test_collectorPolicy.cpp index b5e9c6de6a0..55c03f188cc 100644 --- a/test/hotspot/gtest/gc/shared/test_collectorPolicy.cpp +++ b/test/hotspot/gtest/gc/shared/test_collectorPolicy.cpp @@ -140,17 +140,6 @@ class TestGenCollectorPolicy { ASSERT_EQ(param, NewSize); } }; - - class SetMaxNewSizeCmd : public BinaryExecutor { - public: - SetMaxNewSizeCmd(size_t param1, size_t param2) : BinaryExecutor(param1, param2) { } - void execute() { - size_t heap_alignment = GCArguments::compute_heap_alignment(); - size_t new_size_value = align_up(MaxHeapSize, heap_alignment) - - param1 + param2; - FLAG_SET_CMDLINE(MaxNewSize, new_size_value); - } - }; };