blob: 4f54557eb8d911699f981b8e6beb8ffaf2506ac2 [file] [log] [blame]
/*
* Copyright (c) 2018, 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 "precompiled.hpp"
#include "gc/shared/genArguments.hpp"
#include "gc/shared/generation.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;
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 desired_size < max_minus ? desired_size : max_minus;
}
void GenArguments::initialize_alignments() {
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: " SIZE_FORMAT " less than GenAlignment: " SIZE_FORMAT,
HeapAlignment, GenAlignment);
assert(GenAlignment % SpaceAlignment == 0,
"GenAlignment: " SIZE_FORMAT " not aligned by SpaceAlignment: " SIZE_FORMAT,
GenAlignment, SpaceAlignment);
assert(HeapAlignment % GenAlignment == 0,
"HeapAlignment: " SIZE_FORMAT " not aligned by GenAlignment: " SIZE_FORMAT,
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(size_t, MaxHeapSize, smallest_heap_size);
}
// If needed, synchronize MinHeapSize size and InitialHeapSize
if (MinHeapSize < smallest_heap_size) {
MinHeapSize = smallest_heap_size;
if (InitialHeapSize < MinHeapSize) {
FLAG_SET_ERGO(size_t, 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) {
log_warning(gc, ergo)("NewSize was set larger than initial heap size, will use initial heap size.");
FLAG_SET_ERGO(size_t, NewSize, bound_minus_alignment(NewSize, InitialHeapSize, GenAlignment));
}
// 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(size_t, 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 (" SIZE_FORMAT "k) is equal to or greater than the entire "
"heap (" SIZE_FORMAT "k). A new max generation size of " SIZE_FORMAT "k will be used.",
MaxNewSize/K, MaxHeapSize/K, smaller_max_new_size/K);
}
FLAG_SET_ERGO(size_t, MaxNewSize, smaller_max_new_size);
if (NewSize > MaxNewSize) {
FLAG_SET_ERGO(size_t, NewSize, MaxNewSize);
}
} else if (MaxNewSize < NewSize) {
FLAG_SET_ERGO(size_t, MaxNewSize, NewSize);
} else if (!is_aligned(MaxNewSize, GenAlignment)) {
FLAG_SET_ERGO(size_t, 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 (" SIZE_FORMAT "k) is greater than the MaxNewSize (" SIZE_FORMAT "k). "
"A new max generation size of " SIZE_FORMAT "k will be used.",
NewSize/K, MaxNewSize/K, NewSize/K);
}
FLAG_SET_ERGO(size_t, MaxNewSize, NewSize);
}
if (SurvivorRatio < 1 || NewRatio < 1) {
vm_exit_during_initialization("Invalid young gen ratio specified");
}
if (OldSize < old_gen_size_lower_bound()) {
FLAG_SET_ERGO(size_t, OldSize, old_gen_size_lower_bound());
}
if (!is_aligned(OldSize, GenAlignment)) {
FLAG_SET_ERGO(size_t, OldSize, align_down(OldSize, GenAlignment));
}
if (FLAG_IS_CMDLINE(OldSize) && FLAG_IS_DEFAULT(MaxHeapSize)) {
// NewRatio will be used later to set the young generation size so we use
// it to calculate how big the heap should be based on the requested OldSize
// and NewRatio.
assert(NewRatio > 0, "NewRatio should have been set up earlier");
size_t calculated_heapsize = (OldSize / NewRatio) * (NewRatio + 1);
calculated_heapsize = align_up(calculated_heapsize, HeapAlignment);
FLAG_SET_ERGO(size_t, MaxHeapSize, calculated_heapsize);
FLAG_SET_ERGO(size_t, InitialHeapSize, calculated_heapsize);
}
// 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(size_t, 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.
FLAG_SET_ERGO(size_t, OldSize, MaxHeapSize - NewSize);
} else {
FLAG_SET_ERGO(size_t, MaxHeapSize, align_up(NewSize + OldSize, HeapAlignment));
}
}
// Update NewSize, if possible, to avoid sizing the young gen too small when only
// OldSize is set on the command line.
if (FLAG_IS_CMDLINE(OldSize) && !FLAG_IS_CMDLINE(NewSize)) {
if (OldSize < InitialHeapSize) {
size_t new_size = InitialHeapSize - OldSize;
if (new_size >= MinNewSize && new_size <= MaxNewSize) {
FLAG_SET_ERGO(size_t, NewSize, new_size);
}
}
}
always_do_update_barrier = UseConcMarkSweepGC;
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.
// Minimum sizes of the generations may be different than
// the initial sizes. An inconsistency is permitted here
// in the total size that can be specified explicitly by
// command line specification of OldSize and NewSize and
// also a command line specification of -Xms. Issue a warning
// but allow the values to pass.
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 = MIN2(MAX2(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 =
MIN2(max_young_size, MAX2(scale_by_NewRatio_aligned(InitialHeapSize, GenAlignment), NewSize));
}
}
log_trace(gc, heap)("1: Minimum young " SIZE_FORMAT " Initial young " SIZE_FORMAT " Maximum young " SIZE_FORMAT,
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);
size_t initial_old_size = OldSize;
// If no explicit command line flag has been set for the
// old generation size, use what is left.
if (!FLAG_IS_CMDLINE(OldSize)) {
// The user has not specified any value but the ergonomics
// may have chosen a value (which may or may not be consistent
// with the overall heap size). In either case make
// the minimum, maximum and initial sizes consistent
// with the young sizes and the overall heap sizes.
MinOldSize = GenAlignment;
initial_old_size = MIN2(MaxOldSize, MAX2(InitialHeapSize - initial_young_size, MinOldSize));
// MaxOldSize has already been made consistent above.
} else {
// OldSize has been explicitly set on the command line. Use it
// for the initial size but make sure the minimum allow a young
// generation to fit as well.
// If the user has explicitly set an OldSize that is inconsistent
// with other command line flags, issue a warning.
// The generation minimums and the overall heap minimum should
// be within one generation alignment.
if (initial_old_size > MaxOldSize) {
log_warning(gc, ergo)("Inconsistency between maximum heap size and maximum "
"generation sizes: using maximum heap = " SIZE_FORMAT
", -XX:OldSize flag is being ignored",
MaxHeapSize);
initial_old_size = MaxOldSize;
}
MinOldSize = MIN2(initial_old_size, MinHeapSize - MinNewSize);
}
// 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 " SIZE_FORMAT " Initial young " SIZE_FORMAT " Maximum young " SIZE_FORMAT,
MinNewSize, initial_young_size, max_young_size);
}
// Write back to flags if necessary.
if (NewSize != initial_young_size) {
FLAG_SET_ERGO(size_t, NewSize, initial_young_size);
}
if (MaxNewSize != max_young_size) {
FLAG_SET_ERGO(size_t, MaxNewSize, max_young_size);
}
if (OldSize != initial_old_size) {
FLAG_SET_ERGO(size_t, OldSize, initial_old_size);
}
log_trace(gc, heap)("Minimum old " SIZE_FORMAT " Initial old " SIZE_FORMAT " Maximum old " SIZE_FORMAT,
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