1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 #include "precompiled.hpp"
  26 #include "gc/shared/genArguments.hpp"
  27 #include "gc/shared/generation.hpp"
  28 #include "logging/log.hpp"
  29 #include "runtime/globals_extension.hpp"
  30 #include "runtime/java.hpp"
  31 #include "utilities/align.hpp"
  32 #include "utilities/globalDefinitions.hpp"
  33 
  34 size_t MinNewSize = 0;
  35 
  36 size_t MinOldSize = 0;
  37 size_t MaxOldSize = 0;
  38 
  39 size_t GenAlignment = 0;
  40 
  41 size_t GenArguments::conservative_max_heap_alignment() { return (size_t)Generation::GenGrain; }
  42 
  43 static size_t young_gen_size_lower_bound() {
  44   // The young generation must be aligned and have room for eden + two survivors
  45   return align_up(3 * SpaceAlignment, GenAlignment);
  46 }
  47 
  48 static size_t old_gen_size_lower_bound() {
  49   return align_up(SpaceAlignment, GenAlignment);
  50 }
  51 
  52 size_t GenArguments::scale_by_NewRatio_aligned(size_t base_size, size_t alignment) {
  53   return align_down_bounded(base_size / (NewRatio + 1), alignment);
  54 }
  55 
  56 static size_t bound_minus_alignment(size_t desired_size,
  57                                     size_t maximum_size,
  58                                     size_t alignment) {
  59   size_t max_minus = maximum_size - alignment;
  60   return desired_size < max_minus ? desired_size : max_minus;
  61 }
  62 
  63 void GenArguments::initialize_alignments() {
  64   SpaceAlignment = GenAlignment = (size_t)Generation::GenGrain;
  65   HeapAlignment = compute_heap_alignment();
  66 }
  67 
  68 void GenArguments::initialize_heap_flags_and_sizes() {
  69   GCArguments::initialize_heap_flags_and_sizes();
  70 
  71   assert(GenAlignment != 0, "Generation alignment not set up properly");
  72   assert(HeapAlignment >= GenAlignment,
  73          "HeapAlignment: " SIZE_FORMAT " less than GenAlignment: " SIZE_FORMAT,
  74          HeapAlignment, GenAlignment);
  75   assert(GenAlignment % SpaceAlignment == 0,
  76          "GenAlignment: " SIZE_FORMAT " not aligned by SpaceAlignment: " SIZE_FORMAT,
  77          GenAlignment, SpaceAlignment);
  78   assert(HeapAlignment % GenAlignment == 0,
  79          "HeapAlignment: " SIZE_FORMAT " not aligned by GenAlignment: " SIZE_FORMAT,
  80          HeapAlignment, GenAlignment);
  81 
  82   // All generational heaps have a young gen; handle those flags here
  83 
  84   // Make sure the heap is large enough for two generations
  85   size_t smallest_new_size = young_gen_size_lower_bound();
  86   size_t smallest_heap_size = align_up(smallest_new_size + old_gen_size_lower_bound(),
  87                                        HeapAlignment);
  88   if (MaxHeapSize < smallest_heap_size) {
  89     FLAG_SET_ERGO(MaxHeapSize, smallest_heap_size);
  90   }
  91   // If needed, synchronize MinHeapSize size and InitialHeapSize
  92   if (MinHeapSize < smallest_heap_size) {
  93     FLAG_SET_ERGO(MinHeapSize, smallest_heap_size);
  94     if (InitialHeapSize < MinHeapSize) {
  95       FLAG_SET_ERGO(InitialHeapSize, smallest_heap_size);
  96     }
  97   }
  98 
  99   // Make sure NewSize allows an old generation to fit even if set on the command line
 100   if (FLAG_IS_CMDLINE(NewSize) && NewSize >= InitialHeapSize) {
 101     log_warning(gc, ergo)("NewSize was set larger than initial heap size, will use initial heap size.");
 102     FLAG_SET_ERGO(NewSize, bound_minus_alignment(NewSize, InitialHeapSize, GenAlignment));
 103   }
 104 
 105   // Now take the actual NewSize into account. We will silently increase NewSize
 106   // if the user specified a smaller or unaligned value.
 107   size_t bounded_new_size = bound_minus_alignment(NewSize, MaxHeapSize, GenAlignment);
 108   bounded_new_size = MAX2(smallest_new_size, align_down(bounded_new_size, GenAlignment));
 109   if (bounded_new_size != NewSize) {
 110     FLAG_SET_ERGO(NewSize, bounded_new_size);
 111   }
 112   MinNewSize = smallest_new_size;
 113 
 114   if (!FLAG_IS_DEFAULT(MaxNewSize)) {
 115     if (MaxNewSize >= MaxHeapSize) {
 116       // Make sure there is room for an old generation
 117       size_t smaller_max_new_size = MaxHeapSize - GenAlignment;
 118       if (FLAG_IS_CMDLINE(MaxNewSize)) {
 119         log_warning(gc, ergo)("MaxNewSize (" SIZE_FORMAT "k) is equal to or greater than the entire "
 120                               "heap (" SIZE_FORMAT "k).  A new max generation size of " SIZE_FORMAT "k will be used.",
 121                               MaxNewSize/K, MaxHeapSize/K, smaller_max_new_size/K);
 122       }
 123       FLAG_SET_ERGO(MaxNewSize, smaller_max_new_size);
 124       if (NewSize > MaxNewSize) {
 125         FLAG_SET_ERGO(NewSize, MaxNewSize);
 126       }
 127     } else if (MaxNewSize < NewSize) {
 128       FLAG_SET_ERGO(MaxNewSize, NewSize);
 129     } else if (!is_aligned(MaxNewSize, GenAlignment)) {
 130       FLAG_SET_ERGO(MaxNewSize, align_down(MaxNewSize, GenAlignment));
 131     }
 132   }
 133 
 134   if (NewSize > MaxNewSize) {
 135     // At this point this should only happen if the user specifies a large NewSize and/or
 136     // a small (but not too small) MaxNewSize.
 137     if (FLAG_IS_CMDLINE(MaxNewSize)) {
 138       log_warning(gc, ergo)("NewSize (" SIZE_FORMAT "k) is greater than the MaxNewSize (" SIZE_FORMAT "k). "
 139                             "A new max generation size of " SIZE_FORMAT "k will be used.",
 140                             NewSize/K, MaxNewSize/K, NewSize/K);
 141     }
 142     FLAG_SET_ERGO(MaxNewSize, NewSize);
 143   }
 144 
 145   if (SurvivorRatio < 1 || NewRatio < 1) {
 146     vm_exit_during_initialization("Invalid young gen ratio specified");
 147   }
 148 
 149   if (OldSize < old_gen_size_lower_bound()) {
 150     FLAG_SET_ERGO(OldSize, old_gen_size_lower_bound());
 151   }
 152   if (!is_aligned(OldSize, GenAlignment)) {
 153     FLAG_SET_ERGO(OldSize, align_down(OldSize, GenAlignment));
 154   }
 155 
 156   if (FLAG_IS_CMDLINE(OldSize) && FLAG_IS_DEFAULT(MaxHeapSize)) {
 157     // NewRatio will be used later to set the young generation size so we use
 158     // it to calculate how big the heap should be based on the requested OldSize
 159     // and NewRatio.
 160     assert(NewRatio > 0, "NewRatio should have been set up earlier");
 161     size_t calculated_heapsize = (OldSize / NewRatio) * (NewRatio + 1);
 162 
 163     calculated_heapsize = align_up(calculated_heapsize, HeapAlignment);
 164     FLAG_SET_ERGO(MaxHeapSize, calculated_heapsize);
 165     FLAG_SET_ERGO(InitialHeapSize, calculated_heapsize);
 166   }
 167 
 168   // Adjust NewSize and OldSize or MaxHeapSize to match each other
 169   if (NewSize + OldSize > MaxHeapSize) {
 170     if (FLAG_IS_CMDLINE(MaxHeapSize)) {
 171       // Somebody has set a maximum heap size with the intention that we should not
 172       // exceed it. Adjust New/OldSize as necessary.
 173       size_t calculated_size = NewSize + OldSize;
 174       double shrink_factor = (double) MaxHeapSize / calculated_size;
 175       size_t smaller_new_size = align_down((size_t)(NewSize * shrink_factor), GenAlignment);
 176       FLAG_SET_ERGO(NewSize, MAX2(young_gen_size_lower_bound(), smaller_new_size));
 177 
 178       // OldSize is already aligned because above we aligned MaxHeapSize to
 179       // HeapAlignment, and we just made sure that NewSize is aligned to
 180       // GenAlignment. In initialize_flags() we verified that HeapAlignment
 181       // is a multiple of GenAlignment.
 182       FLAG_SET_ERGO(OldSize, MaxHeapSize - NewSize);
 183     } else {
 184       FLAG_SET_ERGO(MaxHeapSize, align_up(NewSize + OldSize, HeapAlignment));
 185     }
 186   }
 187 
 188   // Update NewSize, if possible, to avoid sizing the young gen too small when only
 189   // OldSize is set on the command line.
 190   if (FLAG_IS_CMDLINE(OldSize) && !FLAG_IS_CMDLINE(NewSize)) {
 191     if (OldSize < InitialHeapSize) {
 192       size_t new_size = InitialHeapSize - OldSize;
 193       if (new_size >= MinNewSize && new_size <= MaxNewSize) {
 194         FLAG_SET_ERGO(NewSize, new_size);
 195       }
 196     }
 197   }
 198 
 199   DEBUG_ONLY(assert_flags();)
 200 }
 201 
 202 // Values set on the command line win over any ergonomically
 203 // set command line parameters.
 204 // Ergonomic choice of parameters are done before this
 205 // method is called.  Values for command line parameters such as NewSize
 206 // and MaxNewSize feed those ergonomic choices into this method.
 207 // This method makes the final generation sizings consistent with
 208 // themselves and with overall heap sizings.
 209 // In the absence of explicitly set command line flags, policies
 210 // such as the use of NewRatio are used to size the generation.
 211 
 212 // Minimum sizes of the generations may be different than
 213 // the initial sizes.  An inconsistency is permitted here
 214 // in the total size that can be specified explicitly by
 215 // command line specification of OldSize and NewSize and
 216 // also a command line specification of -Xms.  Issue a warning
 217 // but allow the values to pass.
 218 void GenArguments::initialize_size_info() {
 219   GCArguments::initialize_size_info();
 220 
 221   size_t max_young_size = MaxNewSize;
 222 
 223   // Determine maximum size of the young generation.
 224 
 225   if (FLAG_IS_DEFAULT(MaxNewSize)) {
 226     max_young_size = scale_by_NewRatio_aligned(MaxHeapSize, GenAlignment);
 227     // Bound the maximum size by NewSize below (since it historically
 228     // would have been NewSize and because the NewRatio calculation could
 229     // yield a size that is too small) and bound it by MaxNewSize above.
 230     // Ergonomics plays here by previously calculating the desired
 231     // NewSize and MaxNewSize.
 232     max_young_size = clamp(max_young_size, NewSize, MaxNewSize);
 233   }
 234 
 235   // Given the maximum young size, determine the initial and
 236   // minimum young sizes.
 237   size_t initial_young_size = NewSize;
 238 
 239   if (MaxHeapSize == InitialHeapSize) {
 240     // The maximum and initial heap sizes are the same so the generation's
 241     // initial size must be the same as it maximum size. Use NewSize as the
 242     // size if set on command line.
 243     max_young_size = FLAG_IS_CMDLINE(NewSize) ? NewSize : max_young_size;
 244     initial_young_size = max_young_size;
 245 
 246     // Also update the minimum size if min == initial == max.
 247     if (MaxHeapSize == MinHeapSize) {
 248       MinNewSize = max_young_size;
 249     }
 250   } else {
 251     if (FLAG_IS_CMDLINE(NewSize)) {
 252       // If NewSize is set on the command line, we should use it as
 253       // the initial size, but make sure it is within the heap bounds.
 254       initial_young_size =
 255         MIN2(max_young_size, bound_minus_alignment(NewSize, InitialHeapSize, GenAlignment));
 256       MinNewSize = bound_minus_alignment(initial_young_size, MinHeapSize, GenAlignment);
 257     } else {
 258       // For the case where NewSize is not set on the command line, use
 259       // NewRatio to size the initial generation size. Use the current
 260       // NewSize as the floor, because if NewRatio is overly large, the resulting
 261       // size can be too small.
 262       initial_young_size =
 263         clamp(scale_by_NewRatio_aligned(InitialHeapSize, GenAlignment), NewSize, max_young_size);
 264     }
 265   }
 266 
 267   log_trace(gc, heap)("1: Minimum young " SIZE_FORMAT "  Initial young " SIZE_FORMAT "  Maximum young " SIZE_FORMAT,
 268                       MinNewSize, initial_young_size, max_young_size);
 269 
 270   // At this point the minimum, initial and maximum sizes
 271   // of the overall heap and of the young generation have been determined.
 272   // The maximum old size can be determined from the maximum young
 273   // and maximum heap size since no explicit flags exist
 274   // for setting the old generation maximum.
 275   MaxOldSize = MAX2(MaxHeapSize - max_young_size, GenAlignment);
 276 
 277   size_t initial_old_size = OldSize;
 278 
 279   // If no explicit command line flag has been set for the
 280   // old generation size, use what is left.
 281   if (!FLAG_IS_CMDLINE(OldSize)) {
 282     // The user has not specified any value but the ergonomics
 283     // may have chosen a value (which may or may not be consistent
 284     // with the overall heap size).  In either case make
 285     // the minimum, maximum and initial sizes consistent
 286     // with the young sizes and the overall heap sizes.
 287     MinOldSize = GenAlignment;
 288     initial_old_size = clamp(InitialHeapSize - initial_young_size, MinOldSize, MaxOldSize);
 289     // MaxOldSize has already been made consistent above.
 290   } else {
 291     // OldSize has been explicitly set on the command line. Use it
 292     // for the initial size but make sure the minimum allow a young
 293     // generation to fit as well.
 294     // If the user has explicitly set an OldSize that is inconsistent
 295     // with other command line flags, issue a warning.
 296     // The generation minimums and the overall heap minimum should
 297     // be within one generation alignment.
 298     if (initial_old_size > MaxOldSize) {
 299       log_warning(gc, ergo)("Inconsistency between maximum heap size and maximum "
 300                             "generation sizes: using maximum heap = " SIZE_FORMAT
 301                             ", -XX:OldSize flag is being ignored",
 302                             MaxHeapSize);
 303       initial_old_size = MaxOldSize;
 304     }
 305 
 306     MinOldSize = MIN2(initial_old_size, MinHeapSize - MinNewSize);
 307   }
 308 
 309   // The initial generation sizes should match the initial heap size,
 310   // if not issue a warning and resize the generations. This behavior
 311   // differs from JDK8 where the generation sizes have higher priority
 312   // than the initial heap size.
 313   if ((initial_old_size + initial_young_size) != InitialHeapSize) {
 314     log_warning(gc, ergo)("Inconsistency between generation sizes and heap size, resizing "
 315                           "the generations to fit the heap.");
 316 
 317     size_t desired_young_size = InitialHeapSize - initial_old_size;
 318     if (InitialHeapSize < initial_old_size) {
 319       // Old want all memory, use minimum for young and rest for old
 320       initial_young_size = MinNewSize;
 321       initial_old_size = InitialHeapSize - MinNewSize;
 322     } else if (desired_young_size > max_young_size) {
 323       // Need to increase both young and old generation
 324       initial_young_size = max_young_size;
 325       initial_old_size = InitialHeapSize - max_young_size;
 326     } else if (desired_young_size < MinNewSize) {
 327       // Need to decrease both young and old generation
 328       initial_young_size = MinNewSize;
 329       initial_old_size = InitialHeapSize - MinNewSize;
 330     } else {
 331       // The young generation boundaries allow us to only update the
 332       // young generation.
 333       initial_young_size = desired_young_size;
 334     }
 335 
 336     log_trace(gc, heap)("2: Minimum young " SIZE_FORMAT "  Initial young " SIZE_FORMAT "  Maximum young " SIZE_FORMAT,
 337                         MinNewSize, initial_young_size, max_young_size);
 338   }
 339 
 340   // Write back to flags if necessary.
 341   if (NewSize != initial_young_size) {
 342     FLAG_SET_ERGO(NewSize, initial_young_size);
 343   }
 344 
 345   if (MaxNewSize != max_young_size) {
 346     FLAG_SET_ERGO(MaxNewSize, max_young_size);
 347   }
 348 
 349   if (OldSize != initial_old_size) {
 350     FLAG_SET_ERGO(OldSize, initial_old_size);
 351   }
 352 
 353   log_trace(gc, heap)("Minimum old " SIZE_FORMAT "  Initial old " SIZE_FORMAT "  Maximum old " SIZE_FORMAT,
 354                       MinOldSize, OldSize, MaxOldSize);
 355 
 356   DEBUG_ONLY(assert_size_info();)
 357 }
 358 
 359 #ifdef ASSERT
 360 void GenArguments::assert_flags() {
 361   GCArguments::assert_flags();
 362   assert(NewSize >= MinNewSize, "Ergonomics decided on a too small young gen size");
 363   assert(NewSize <= MaxNewSize, "Ergonomics decided on incompatible initial and maximum young gen sizes");
 364   assert(FLAG_IS_DEFAULT(MaxNewSize) || MaxNewSize < MaxHeapSize, "Ergonomics decided on incompatible maximum young gen and heap sizes");
 365   assert(NewSize % GenAlignment == 0, "NewSize alignment");
 366   assert(FLAG_IS_DEFAULT(MaxNewSize) || MaxNewSize % GenAlignment == 0, "MaxNewSize alignment");
 367   assert(OldSize + NewSize <= MaxHeapSize, "Ergonomics decided on incompatible generation and heap sizes");
 368   assert(OldSize % GenAlignment == 0, "OldSize alignment");
 369 }
 370 
 371 void GenArguments::assert_size_info() {
 372   GCArguments::assert_size_info();
 373   // GenArguments::initialize_size_info may update the MaxNewSize
 374   assert(MaxNewSize < MaxHeapSize, "Ergonomics decided on incompatible maximum young and heap sizes");
 375   assert(MinNewSize <= NewSize, "Ergonomics decided on incompatible minimum and initial young gen sizes");
 376   assert(NewSize <= MaxNewSize, "Ergonomics decided on incompatible initial and maximum young gen sizes");
 377   assert(MinNewSize % GenAlignment == 0, "_min_young_size alignment");
 378   assert(NewSize % GenAlignment == 0, "_initial_young_size alignment");
 379   assert(MaxNewSize % GenAlignment == 0, "MaxNewSize alignment");
 380   assert(MinNewSize <= bound_minus_alignment(MinNewSize, MinHeapSize, GenAlignment),
 381       "Ergonomics made minimum young generation larger than minimum heap");
 382   assert(NewSize <=  bound_minus_alignment(NewSize, InitialHeapSize, GenAlignment),
 383       "Ergonomics made initial young generation larger than initial heap");
 384   assert(MaxNewSize <= bound_minus_alignment(MaxNewSize, MaxHeapSize, GenAlignment),
 385       "Ergonomics made maximum young generation lager than maximum heap");
 386   assert(MinOldSize <= OldSize, "Ergonomics decided on incompatible minimum and initial old gen sizes");
 387   assert(OldSize <= MaxOldSize, "Ergonomics decided on incompatible initial and maximum old gen sizes");
 388   assert(MaxOldSize % GenAlignment == 0, "MaxOldSize alignment");
 389   assert(OldSize % GenAlignment == 0, "OldSize alignment");
 390   assert(MaxHeapSize <= (MaxNewSize + MaxOldSize), "Total maximum heap sizes must be sum of generation maximum sizes");
 391   assert(MinNewSize + MinOldSize <= MinHeapSize, "Minimum generation sizes exceed minimum heap size");
 392   assert(NewSize + OldSize == InitialHeapSize, "Initial generation sizes should match initial heap size");
 393   assert(MaxNewSize + MaxOldSize == MaxHeapSize, "Maximum generation sizes should match maximum heap size");
 394 }
 395 #endif // ASSERT