| /* | |
| * Copyright (c) 2018, 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 | |
| * 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. | |
| * | |
| */ | |
| #include "gc/serial/generation.hpp" | |
| #include "gc/shared/cardTable.hpp" | |
| #include "gc/shared/genArguments.hpp" | |
| #include "logging/log.hpp" | |
| #include "runtime/globals_extension.hpp" | |
| #include "runtime/java.hpp" | |
| #include "utilities/align.hpp" | |
| #include "utilities/globalDefinitions.hpp" | |
| size_t MinNewSize = 0; | |
| size_t MinOldSize = 0; | |
| size_t MaxOldSize = 0; | |
| // If InitialHeapSize or MinHeapSize is not set on cmdline, this variable, | |
| // together with NewSize, is used to derive them. | |
| // Using the same value when it was a configurable flag to avoid breakage. | |
| // See more in JDK-8346005 | |
| size_t OldSize = ScaleForWordSize(4*M); | |
| size_t GenAlignment = 0; | |
| 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 align_up(3 * SpaceAlignment, GenAlignment); | |
| } | |
| static size_t old_gen_size_lower_bound() { | |
| return align_up(SpaceAlignment, GenAlignment); | |
| } | |
| size_t GenArguments::scale_by_NewRatio_aligned(size_t base_size, size_t alignment) { | |
| return align_down_bounded(base_size / (NewRatio + 1), alignment); | |
| } | |
| static size_t bound_minus_alignment(size_t desired_size, | |
| size_t maximum_size, | |
| size_t alignment) { | |
| size_t max_minus = maximum_size - alignment; | |
| return MIN2(desired_size, max_minus); | |
| } | |
| void GenArguments::initialize_alignments() { | |
| // Initialize card size before initializing alignments | |
| CardTable::initialize_card_size(); | |
| SpaceAlignment = GenAlignment = (size_t)Generation::GenGrain; | |
| HeapAlignment = compute_heap_alignment(); | |
| } | |
| void GenArguments::initialize_heap_flags_and_sizes() { | |
| GCArguments::initialize_heap_flags_and_sizes(); | |
| assert(GenAlignment != 0, "Generation alignment not set up properly"); | |
| assert(HeapAlignment >= GenAlignment, | |
| "HeapAlignment: %zu less than GenAlignment: %zu", | |
| HeapAlignment, GenAlignment); | |
| assert(GenAlignment % SpaceAlignment == 0, | |
| "GenAlignment: %zu not aligned by SpaceAlignment: %zu", | |
| GenAlignment, SpaceAlignment); | |
| assert(HeapAlignment % GenAlignment == 0, | |
| "HeapAlignment: %zu not aligned by GenAlignment: %zu", | |
| HeapAlignment, GenAlignment); | |
| // All generational heaps have a young gen; handle those flags here | |
| // Make sure the heap is large enough for two generations | |
| size_t smallest_new_size = young_gen_size_lower_bound(); | |
| size_t smallest_heap_size = align_up(smallest_new_size + old_gen_size_lower_bound(), | |
| HeapAlignment); | |
| if (MaxHeapSize < smallest_heap_size) { | |
| FLAG_SET_ERGO(MaxHeapSize, smallest_heap_size); | |
| } | |
| // If needed, synchronize MinHeapSize size and InitialHeapSize | |
| if (MinHeapSize < smallest_heap_size) { | |
| FLAG_SET_ERGO(MinHeapSize, smallest_heap_size); | |
| if (InitialHeapSize < MinHeapSize) { | |
| FLAG_SET_ERGO(InitialHeapSize, smallest_heap_size); | |
| } | |
| } | |
| // Make sure NewSize allows an old generation to fit even if set on the command line | |
| if (FLAG_IS_CMDLINE(NewSize) && NewSize >= InitialHeapSize) { | |
| size_t revised_new_size = bound_minus_alignment(NewSize, InitialHeapSize, GenAlignment); | |
| log_warning(gc, ergo)("NewSize (%zuk) is equal to or greater than initial heap size (%zuk). A new " | |
| "NewSize of %zuk will be used to accomodate an old generation.", | |
| NewSize/K, InitialHeapSize/K, revised_new_size/K); | |
| FLAG_SET_ERGO(NewSize, revised_new_size); | |
| } | |
| // Now take the actual NewSize into account. We will silently increase NewSize | |
| // if the user specified a smaller or unaligned value. | |
| size_t bounded_new_size = bound_minus_alignment(NewSize, MaxHeapSize, GenAlignment); | |
| bounded_new_size = MAX2(smallest_new_size, align_down(bounded_new_size, GenAlignment)); | |
| if (bounded_new_size != NewSize) { | |
| FLAG_SET_ERGO(NewSize, bounded_new_size); | |
| } | |
| MinNewSize = smallest_new_size; | |
| if (!FLAG_IS_DEFAULT(MaxNewSize)) { | |
| if (MaxNewSize >= MaxHeapSize) { | |
| // Make sure there is room for an old generation | |
| size_t smaller_max_new_size = MaxHeapSize - GenAlignment; | |
| if (FLAG_IS_CMDLINE(MaxNewSize)) { | |
| log_warning(gc, ergo)("MaxNewSize (%zuk) is equal to or greater than the entire " | |
| "heap (%zuk). A new max generation size of %zuk will be used.", | |
| MaxNewSize/K, MaxHeapSize/K, smaller_max_new_size/K); | |
| } | |
| FLAG_SET_ERGO(MaxNewSize, smaller_max_new_size); | |
| if (NewSize > MaxNewSize) { | |
| FLAG_SET_ERGO(NewSize, MaxNewSize); | |
| } | |
| } else if (MaxNewSize < NewSize) { | |
| FLAG_SET_ERGO(MaxNewSize, NewSize); | |
| } else if (!is_aligned(MaxNewSize, GenAlignment)) { | |
| FLAG_SET_ERGO(MaxNewSize, align_down(MaxNewSize, GenAlignment)); | |
| } | |
| } | |
| if (NewSize > MaxNewSize) { | |
| // At this point this should only happen if the user specifies a large NewSize and/or | |
| // a small (but not too small) MaxNewSize. | |
| if (FLAG_IS_CMDLINE(MaxNewSize)) { | |
| log_warning(gc, ergo)("NewSize (%zuk) is greater than the MaxNewSize (%zuk). " | |
| "A new max generation size of %zuk will be used.", | |
| NewSize/K, MaxNewSize/K, NewSize/K); | |
| } | |
| FLAG_SET_ERGO(MaxNewSize, NewSize); | |
| } | |
| if (SurvivorRatio < 1 || NewRatio < 1) { | |
| vm_exit_during_initialization("Invalid young gen ratio specified"); | |
| } | |
| OldSize = old_gen_size_lower_bound(); | |
| // Adjust NewSize and OldSize or MaxHeapSize to match each other | |
| if (NewSize + OldSize > MaxHeapSize) { | |
| if (FLAG_IS_CMDLINE(MaxHeapSize)) { | |
| // Somebody has set a maximum heap size with the intention that we should not | |
| // exceed it. Adjust New/OldSize as necessary. | |
| size_t calculated_size = NewSize + OldSize; | |
| double shrink_factor = (double) MaxHeapSize / calculated_size; | |
| size_t smaller_new_size = align_down((size_t)(NewSize * shrink_factor), GenAlignment); | |
| FLAG_SET_ERGO(NewSize, MAX2(young_gen_size_lower_bound(), smaller_new_size)); | |
| // OldSize is already aligned because above we aligned MaxHeapSize to | |
| // HeapAlignment, and we just made sure that NewSize is aligned to | |
| // GenAlignment. In initialize_flags() we verified that HeapAlignment | |
| // is a multiple of GenAlignment. | |
| OldSize = MaxHeapSize - NewSize; | |
| } else { | |
| FLAG_SET_ERGO(MaxHeapSize, align_up(NewSize + OldSize, HeapAlignment)); | |
| } | |
| } | |
| DEBUG_ONLY(assert_flags();) | |
| } | |
| // Values set on the command line win over any ergonomically | |
| // set command line parameters. | |
| // Ergonomic choice of parameters are done before this | |
| // method is called. Values for command line parameters such as NewSize | |
| // and MaxNewSize feed those ergonomic choices into this method. | |
| // This method makes the final generation sizings consistent with | |
| // themselves and with overall heap sizings. | |
| // In the absence of explicitly set command line flags, policies | |
| // such as the use of NewRatio are used to size the generation. | |
| void GenArguments::initialize_size_info() { | |
| GCArguments::initialize_size_info(); | |
| size_t max_young_size = MaxNewSize; | |
| // Determine maximum size of the young generation. | |
| if (FLAG_IS_DEFAULT(MaxNewSize)) { | |
| max_young_size = scale_by_NewRatio_aligned(MaxHeapSize, GenAlignment); | |
| // Bound the maximum size by NewSize below (since it historically | |
| // would have been NewSize and because the NewRatio calculation could | |
| // yield a size that is too small) and bound it by MaxNewSize above. | |
| // Ergonomics plays here by previously calculating the desired | |
| // NewSize and MaxNewSize. | |
| max_young_size = clamp(max_young_size, NewSize, MaxNewSize); | |
| } | |
| // Given the maximum young size, determine the initial and | |
| // minimum young sizes. | |
| size_t initial_young_size = NewSize; | |
| if (MaxHeapSize == InitialHeapSize) { | |
| // The maximum and initial heap sizes are the same so the generation's | |
| // initial size must be the same as it maximum size. Use NewSize as the | |
| // size if set on command line. | |
| max_young_size = FLAG_IS_CMDLINE(NewSize) ? NewSize : max_young_size; | |
| initial_young_size = max_young_size; | |
| // Also update the minimum size if min == initial == max. | |
| if (MaxHeapSize == MinHeapSize) { | |
| MinNewSize = max_young_size; | |
| } | |
| } else { | |
| if (FLAG_IS_CMDLINE(NewSize)) { | |
| // If NewSize is set on the command line, we should use it as | |
| // the initial size, but make sure it is within the heap bounds. | |
| initial_young_size = | |
| MIN2(max_young_size, bound_minus_alignment(NewSize, InitialHeapSize, GenAlignment)); | |
| MinNewSize = bound_minus_alignment(initial_young_size, MinHeapSize, GenAlignment); | |
| } else { | |
| // For the case where NewSize is not set on the command line, use | |
| // NewRatio to size the initial generation size. Use the current | |
| // NewSize as the floor, because if NewRatio is overly large, the resulting | |
| // size can be too small. | |
| initial_young_size = | |
| clamp(scale_by_NewRatio_aligned(InitialHeapSize, GenAlignment), NewSize, max_young_size); | |
| // Derive MinNewSize from MinHeapSize | |
| MinNewSize = MIN2(scale_by_NewRatio_aligned(MinHeapSize, GenAlignment), initial_young_size); | |
| } | |
| } | |
| log_trace(gc, heap)("1: Minimum young %zu Initial young %zu Maximum young %zu", | |
| MinNewSize, initial_young_size, max_young_size); | |
| // At this point the minimum, initial and maximum sizes | |
| // of the overall heap and of the young generation have been determined. | |
| // The maximum old size can be determined from the maximum young | |
| // and maximum heap size since no explicit flags exist | |
| // for setting the old generation maximum. | |
| MaxOldSize = MAX2(MaxHeapSize - max_young_size, GenAlignment); | |
| MinOldSize = MIN3(MaxOldSize, | |
| InitialHeapSize - initial_young_size, | |
| MinHeapSize - MinNewSize); | |
| size_t initial_old_size = clamp(InitialHeapSize - initial_young_size, MinOldSize, MaxOldSize);; | |
| // The initial generation sizes should match the initial heap size, | |
| // if not issue a warning and resize the generations. This behavior | |
| // differs from JDK8 where the generation sizes have higher priority | |
| // than the initial heap size. | |
| if ((initial_old_size + initial_young_size) != InitialHeapSize) { | |
| log_warning(gc, ergo)("Inconsistency between generation sizes and heap size, resizing " | |
| "the generations to fit the heap."); | |
| size_t desired_young_size = InitialHeapSize - initial_old_size; | |
| if (InitialHeapSize < initial_old_size) { | |
| // Old want all memory, use minimum for young and rest for old | |
| initial_young_size = MinNewSize; | |
| initial_old_size = InitialHeapSize - MinNewSize; | |
| } else if (desired_young_size > max_young_size) { | |
| // Need to increase both young and old generation | |
| initial_young_size = max_young_size; | |
| initial_old_size = InitialHeapSize - max_young_size; | |
| } else if (desired_young_size < MinNewSize) { | |
| // Need to decrease both young and old generation | |
| initial_young_size = MinNewSize; | |
| initial_old_size = InitialHeapSize - MinNewSize; | |
| } else { | |
| // The young generation boundaries allow us to only update the | |
| // young generation. | |
| initial_young_size = desired_young_size; | |
| } | |
| log_trace(gc, heap)("2: Minimum young %zu Initial young %zu Maximum young %zu", | |
| MinNewSize, initial_young_size, max_young_size); | |
| } | |
| // Write back to flags if necessary. | |
| if (NewSize != initial_young_size) { | |
| FLAG_SET_ERGO(NewSize, initial_young_size); | |
| } | |
| if (MaxNewSize != max_young_size) { | |
| FLAG_SET_ERGO(MaxNewSize, max_young_size); | |
| } | |
| if (OldSize != initial_old_size) { | |
| OldSize = initial_old_size; | |
| } | |
| log_trace(gc, heap)("Minimum old %zu Initial old %zu Maximum old %zu", | |
| MinOldSize, OldSize, MaxOldSize); | |
| DEBUG_ONLY(assert_size_info();) | |
| } | |
| #ifdef ASSERT | |
| void GenArguments::assert_flags() { | |
| GCArguments::assert_flags(); | |
| assert(NewSize >= MinNewSize, "Ergonomics decided on a too small young gen size"); | |
| assert(NewSize <= MaxNewSize, "Ergonomics decided on incompatible initial and maximum young gen sizes"); | |
| assert(FLAG_IS_DEFAULT(MaxNewSize) || MaxNewSize < MaxHeapSize, "Ergonomics decided on incompatible maximum young gen and heap sizes"); | |
| assert(NewSize % GenAlignment == 0, "NewSize alignment"); | |
| assert(FLAG_IS_DEFAULT(MaxNewSize) || MaxNewSize % GenAlignment == 0, "MaxNewSize alignment"); | |
| assert(OldSize + NewSize <= MaxHeapSize, "Ergonomics decided on incompatible generation and heap sizes"); | |
| assert(OldSize % GenAlignment == 0, "OldSize alignment"); | |
| } | |
| void GenArguments::assert_size_info() { | |
| GCArguments::assert_size_info(); | |
| // GenArguments::initialize_size_info may update the MaxNewSize | |
| assert(MaxNewSize < MaxHeapSize, "Ergonomics decided on incompatible maximum young and heap sizes"); | |
| assert(MinNewSize <= NewSize, "Ergonomics decided on incompatible minimum and initial young gen sizes"); | |
| assert(NewSize <= MaxNewSize, "Ergonomics decided on incompatible initial and maximum young gen sizes"); | |
| assert(MinNewSize % GenAlignment == 0, "_min_young_size alignment"); | |
| assert(NewSize % GenAlignment == 0, "_initial_young_size alignment"); | |
| assert(MaxNewSize % GenAlignment == 0, "MaxNewSize alignment"); | |
| assert(MinNewSize <= bound_minus_alignment(MinNewSize, MinHeapSize, GenAlignment), | |
| "Ergonomics made minimum young generation larger than minimum heap"); | |
| assert(NewSize <= bound_minus_alignment(NewSize, InitialHeapSize, GenAlignment), | |
| "Ergonomics made initial young generation larger than initial heap"); | |
| assert(MaxNewSize <= bound_minus_alignment(MaxNewSize, MaxHeapSize, GenAlignment), | |
| "Ergonomics made maximum young generation lager than maximum heap"); | |
| assert(MinOldSize <= OldSize, "Ergonomics decided on incompatible minimum and initial old gen sizes"); | |
| assert(OldSize <= MaxOldSize, "Ergonomics decided on incompatible initial and maximum old gen sizes"); | |
| assert(MaxOldSize % GenAlignment == 0, "MaxOldSize alignment"); | |
| assert(OldSize % GenAlignment == 0, "OldSize alignment"); | |
| assert(MaxHeapSize <= (MaxNewSize + MaxOldSize), "Total maximum heap sizes must be sum of generation maximum sizes"); | |
| assert(MinNewSize + MinOldSize <= MinHeapSize, "Minimum generation sizes exceed minimum heap size"); | |
| assert(NewSize + OldSize == InitialHeapSize, "Initial generation sizes should match initial heap size"); | |
| assert(MaxNewSize + MaxOldSize == MaxHeapSize, "Maximum generation sizes should match maximum heap size"); | |
| } | |
| #endif // ASSERT |