1 /*
  2  * Copyright Amazon.com Inc. 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 
 27 #include "gc/shenandoah/shenandoahGeneration.hpp"
 28 #include "gc/shenandoah/shenandoahGenerationSizer.hpp"
 29 #include "gc/shenandoah/shenandoahHeap.inline.hpp"
 30 #include "gc/shenandoah/shenandoahHeapRegion.hpp"
 31 #include "gc/shenandoah/shenandoahOldGeneration.hpp"
 32 #include "gc/shenandoah/shenandoahYoungGeneration.hpp"
 33 #include "gc/shared/gc_globals.hpp"
 34 #include "logging/log.hpp"
 35 #include "runtime/globals_extension.hpp"
 36 
 37 
 38 ShenandoahGenerationSizer::ShenandoahGenerationSizer()
 39         : _sizer_kind(SizerDefaults),
 40           _min_desired_young_regions(0),
 41           _max_desired_young_regions(0) {
 42 
 43   if (FLAG_IS_CMDLINE(NewRatio)) {
 44     if (FLAG_IS_CMDLINE(NewSize) || FLAG_IS_CMDLINE(MaxNewSize)) {
 45       log_warning(gc, ergo)("-XX:NewSize and -XX:MaxNewSize override -XX:NewRatio");
 46     } else {
 47       _sizer_kind = SizerNewRatio;
 48       return;
 49     }
 50   }
 51 
 52   if (NewSize > MaxNewSize) {
 53     if (FLAG_IS_CMDLINE(MaxNewSize)) {
 54       log_warning(gc, ergo)("NewSize (" SIZE_FORMAT "k) is greater than the MaxNewSize (" SIZE_FORMAT "k). "
 55                             "A new max generation size of " SIZE_FORMAT "k will be used.",
 56               NewSize/K, MaxNewSize/K, NewSize/K);
 57     }
 58     FLAG_SET_ERGO(MaxNewSize, NewSize);
 59   }
 60 
 61   if (FLAG_IS_CMDLINE(NewSize)) {
 62     _min_desired_young_regions = MAX2(uint(NewSize / ShenandoahHeapRegion::region_size_bytes()), 1U);
 63     if (FLAG_IS_CMDLINE(MaxNewSize)) {
 64       _max_desired_young_regions = MAX2(uint(MaxNewSize / ShenandoahHeapRegion::region_size_bytes()), 1U);
 65       _sizer_kind = SizerMaxAndNewSize;
 66     } else {
 67       _sizer_kind = SizerNewSizeOnly;
 68     }
 69   } else if (FLAG_IS_CMDLINE(MaxNewSize)) {
 70     _max_desired_young_regions = MAX2(uint(MaxNewSize / ShenandoahHeapRegion::region_size_bytes()), 1U);
 71     _sizer_kind = SizerMaxNewSizeOnly;
 72   }
 73 }
 74 
 75 size_t ShenandoahGenerationSizer::calculate_min_young_regions(size_t heap_region_count) {
 76   size_t min_young_regions = (heap_region_count * ShenandoahMinYoungPercentage) / 100;
 77   return MAX2(min_young_regions, (size_t) 1U);
 78 }
 79 
 80 size_t ShenandoahGenerationSizer::calculate_max_young_regions(size_t heap_region_count) {
 81   size_t max_young_regions = (heap_region_count * ShenandoahMaxYoungPercentage) / 100;
 82   return MAX2(max_young_regions, (size_t) 1U);
 83 }
 84 
 85 void ShenandoahGenerationSizer::recalculate_min_max_young_length(size_t heap_region_count) {
 86   assert(heap_region_count > 0, "Heap must be initialized");
 87 
 88   switch (_sizer_kind) {
 89     case SizerDefaults:
 90       _min_desired_young_regions = calculate_min_young_regions(heap_region_count);
 91       _max_desired_young_regions = calculate_max_young_regions(heap_region_count);
 92       break;
 93     case SizerNewSizeOnly:
 94       _max_desired_young_regions = calculate_max_young_regions(heap_region_count);
 95       _max_desired_young_regions = MAX2(_min_desired_young_regions, _max_desired_young_regions);
 96       break;
 97     case SizerMaxNewSizeOnly:
 98       _min_desired_young_regions = calculate_min_young_regions(heap_region_count);
 99       _min_desired_young_regions = MIN2(_min_desired_young_regions, _max_desired_young_regions);
100       break;
101     case SizerMaxAndNewSize:
102       // Do nothing. Values set on the command line, don't update them at runtime.
103       break;
104     case SizerNewRatio:
105       _min_desired_young_regions = MAX2(uint(heap_region_count / (NewRatio + 1)), 1U);
106       _max_desired_young_regions = _min_desired_young_regions;
107       break;
108     default:
109       ShouldNotReachHere();
110   }
111 
112   assert(_min_desired_young_regions <= _max_desired_young_regions, "Invalid min/max young gen size values");
113 }
114 
115 void ShenandoahGenerationSizer::heap_size_changed(size_t heap_size) {
116   recalculate_min_max_young_length(heap_size / ShenandoahHeapRegion::region_size_bytes());
117 }
118 
119 bool ShenandoahGenerationSizer::transfer_regions(ShenandoahGeneration* src, ShenandoahGeneration* dst, size_t regions) const {
120   const size_t bytes_to_transfer = regions * ShenandoahHeapRegion::region_size_bytes();
121 
122   if (src->free_unaffiliated_regions() < regions) {
123     // Source does not have enough free regions for this transfer. The caller should have
124     // already capped the transfer based on available unaffiliated regions.
125     return false;
126   }
127 
128   if (dst->max_capacity() + bytes_to_transfer > max_size_for(dst)) {
129     // This transfer would cause the destination generation to grow above its configured maximum size.
130     return false;
131   }
132 
133   if (src->max_capacity() - bytes_to_transfer < min_size_for(src)) {
134     // This transfer would cause the source generation to shrink below its configured minimum size.
135     return false;
136   }
137 
138   src->decrease_capacity(bytes_to_transfer);
139   dst->increase_capacity(bytes_to_transfer);
140   const size_t new_size = dst->max_capacity();
141   log_info(gc, ergo)("Transfer " SIZE_FORMAT " region(s) from %s to %s, yielding increased size: " PROPERFMT,
142                      regions, src->name(), dst->name(), PROPERFMTARGS(new_size));
143   return true;
144 }
145 
146 
147 size_t ShenandoahGenerationSizer::max_size_for(ShenandoahGeneration* generation) const {
148   switch (generation->type()) {
149     case YOUNG:
150       return max_young_size();
151     case OLD:
152       // On the command line, max size of OLD is specified indirectly, by setting a minimum size of young.
153       // OLD is what remains within the heap after YOUNG has been sized.
154       return ShenandoahHeap::heap()->max_capacity() - min_young_size();
155     default:
156       ShouldNotReachHere();
157       return 0;
158   }
159 }
160 
161 size_t ShenandoahGenerationSizer::min_size_for(ShenandoahGeneration* generation) const {
162   switch (generation->type()) {
163     case YOUNG:
164       return min_young_size();
165     case OLD:
166       // On the command line, min size of OLD is specified indirectly, by setting a maximum size of young.
167       // OLD is what remains within the heap after YOUNG has been sized.
168       return ShenandoahHeap::heap()->max_capacity() - max_young_size();
169     default:
170       ShouldNotReachHere();
171       return 0;
172   }
173 }
174 
175 
176 // Returns true iff transfer is successful
177 bool ShenandoahGenerationSizer::transfer_to_old(size_t regions) const {
178   ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap();
179   return transfer_regions(heap->young_generation(), heap->old_generation(), regions);
180 }
181 
182 // This is used when promoting humongous or highly utilized regular regions in place.  It is not required in this situation
183 // that the transferred regions be unaffiliated.
184 void ShenandoahGenerationSizer::force_transfer_to_old(size_t regions) const {
185   ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap();
186   ShenandoahGeneration* old_gen = heap->old_generation();
187   ShenandoahGeneration* young_gen = heap->young_generation();
188   const size_t bytes_to_transfer = regions * ShenandoahHeapRegion::region_size_bytes();
189 
190   young_gen->decrease_capacity(bytes_to_transfer);
191   old_gen->increase_capacity(bytes_to_transfer);
192   const size_t new_size = old_gen->max_capacity();
193   log_info(gc, ergo)("Forcing transfer of " SIZE_FORMAT " region(s) from %s to %s, yielding increased size: " PROPERFMT,
194                      regions, young_gen->name(), old_gen->name(), PROPERFMTARGS(new_size));
195 }
196 
197 
198 bool ShenandoahGenerationSizer::transfer_to_young(size_t regions) const {
199   ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap();
200   return transfer_regions(heap->old_generation(), heap->young_generation(), regions);
201 }
202 
203 size_t ShenandoahGenerationSizer::min_young_size() const {
204   return min_young_regions() * ShenandoahHeapRegion::region_size_bytes();
205 }
206 
207 size_t ShenandoahGenerationSizer::max_young_size() const {
208   return max_young_regions() * ShenandoahHeapRegion::region_size_bytes();
209 }