< prev index next >

src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp

Print this page

  1 /*
  2  * Copyright (c) 2018, 2020, Red Hat, Inc. 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/gcCause.hpp"
 27 #include "gc/shenandoah/shenandoahCollectionSet.inline.hpp"
 28 #include "gc/shenandoah/shenandoahCollectorPolicy.hpp"
 29 #include "gc/shenandoah/shenandoahHeap.inline.hpp"
 30 #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp"
 31 #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp"
 32 #include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp"
 33 #include "logging/log.hpp"
 34 #include "logging/logTag.hpp"
 35 #include "runtime/globals_extension.hpp"

 36 

 37 int ShenandoahHeuristics::compare_by_garbage(RegionData a, RegionData b) {
 38   if (a._garbage > b._garbage)
 39     return -1;
 40   else if (a._garbage < b._garbage)
 41     return 1;
 42   else return 0;
 43 }
 44 
 45 ShenandoahHeuristics::ShenandoahHeuristics(ShenandoahSpaceInfo* space_info) :
 46   _space_info(space_info),
 47   _region_data(nullptr),
 48   _degenerated_cycles_in_a_row(0),
 49   _successful_cycles_in_a_row(0),

 50   _cycle_start(os::elapsedTime()),
 51   _last_cycle_end(0),
 52   _gc_times_learned(0),
 53   _gc_time_penalties(0),
 54   _gc_time_history(new TruncatedSeq(10, ShenandoahAdaptiveDecayFactor)),
 55   _metaspace_oom()
 56 {
 57   // No unloading during concurrent mark? Communicate that to heuristics
 58   if (!ClassUnloadingWithConcurrentMark) {
 59     FLAG_SET_DEFAULT(ShenandoahUnloadClassesFrequency, 0);
 60   }
 61 
 62   size_t num_regions = ShenandoahHeap::heap()->num_regions();
 63   assert(num_regions > 0, "Sanity");
 64 
 65   _region_data = NEW_C_HEAP_ARRAY(RegionData, num_regions, mtGC);
 66 }
 67 
 68 ShenandoahHeuristics::~ShenandoahHeuristics() {
 69   FREE_C_HEAP_ARRAY(RegionGarbage, _region_data);
 70 }
 71 
 72 void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collection_set) {
 73   assert(collection_set->count() == 0, "Must be empty");
 74 
 75   ShenandoahHeap* heap = ShenandoahHeap::heap();
 76 
 77   // Check all pinned regions have updated status before choosing the collection set.
 78   heap->assert_pinned_region_status();
 79 
 80   // Step 1. Build up the region candidates we care about, rejecting losers and accepting winners right away.
 81 
 82   size_t num_regions = heap->num_regions();
 83 
 84   RegionData* candidates = _region_data;
 85 
 86   size_t cand_idx = 0;
 87 
 88   size_t total_garbage = 0;
 89 
 90   size_t immediate_garbage = 0;
 91   size_t immediate_regions = 0;
 92 
 93   size_t free = 0;

 96   ShenandoahMarkingContext* const ctx = heap->complete_marking_context();
 97 
 98   for (size_t i = 0; i < num_regions; i++) {
 99     ShenandoahHeapRegion* region = heap->get_region(i);
100 
101     size_t garbage = region->garbage();
102     total_garbage += garbage;
103 
104     if (region->is_empty()) {
105       free_regions++;
106       free += ShenandoahHeapRegion::region_size_bytes();
107     } else if (region->is_regular()) {
108       if (!region->has_live()) {
109         // We can recycle it right away and put it in the free set.
110         immediate_regions++;
111         immediate_garbage += garbage;
112         region->make_trash_immediate();
113       } else {
114         // This is our candidate for later consideration.
115         candidates[cand_idx]._region = region;
116         candidates[cand_idx]._garbage = garbage;
117         cand_idx++;
118       }
119     } else if (region->is_humongous_start()) {
120       // Reclaim humongous regions here, and count them as the immediate garbage
121 #ifdef ASSERT
122       bool reg_live = region->has_live();
123       bool bm_live = ctx->is_marked(cast_to_oop(region->bottom()));
124       assert(reg_live == bm_live,
125              "Humongous liveness and marks should agree. Region live: %s; Bitmap live: %s; Region Live Words: " SIZE_FORMAT,
126              BOOL_TO_STR(reg_live), BOOL_TO_STR(bm_live), region->get_live_data_words());
127 #endif
128       if (!region->has_live()) {
129         heap->trash_humongous_region_at(region);
130 
131         // Count only the start. Continuations would be counted on "trash" path
132         immediate_regions++;
133         immediate_garbage += garbage;
134       }
135     } else if (region->is_trash()) {
136       // Count in just trashed collection set, during coalesced CM-with-UR
137       immediate_regions++;
138       immediate_garbage += garbage;
139     }
140   }
141 
142   // Step 2. Look back at garbage statistics, and decide if we want to collect anything,
143   // given the amount of immediately reclaimable garbage. If we do, figure out the collection set.
144 
145   assert (immediate_garbage <= total_garbage,
146           "Cannot have more immediate garbage than total garbage: " SIZE_FORMAT "%s vs " SIZE_FORMAT "%s",
147           byte_size_in_proper_unit(immediate_garbage), proper_unit_for_byte_size(immediate_garbage),
148           byte_size_in_proper_unit(total_garbage),     proper_unit_for_byte_size(total_garbage));
149 
150   size_t immediate_percent = (total_garbage == 0) ? 0 : (immediate_garbage * 100 / total_garbage);
151 
152   if (immediate_percent <= ShenandoahImmediateThreshold) {
153     choose_collection_set_from_regiondata(collection_set, candidates, cand_idx, immediate_garbage + free);




154   }
155 
156   size_t cset_percent = (total_garbage == 0) ? 0 : (collection_set->garbage() * 100 / total_garbage);
157 
158   size_t collectable_garbage = collection_set->garbage() + immediate_garbage;
159   size_t collectable_garbage_percent = (total_garbage == 0) ? 0 : (collectable_garbage * 100 / total_garbage);
160 
161   log_info(gc, ergo)("Collectable Garbage: " SIZE_FORMAT "%s (" SIZE_FORMAT "%%), "
162                      "Immediate: " SIZE_FORMAT "%s (" SIZE_FORMAT "%%), "
163                      "CSet: " SIZE_FORMAT "%s (" SIZE_FORMAT "%%)",
164 
165                      byte_size_in_proper_unit(collectable_garbage),
166                      proper_unit_for_byte_size(collectable_garbage),
167                      collectable_garbage_percent,
168 
169                      byte_size_in_proper_unit(immediate_garbage),
170                      proper_unit_for_byte_size(immediate_garbage),
171                      immediate_percent,

172 
173                      byte_size_in_proper_unit(collection_set->garbage()),
174                      proper_unit_for_byte_size(collection_set->garbage()),
175                      cset_percent);

176 }
177 
178 void ShenandoahHeuristics::record_cycle_start() {
179   _cycle_start = os::elapsedTime();
180 }
181 
182 void ShenandoahHeuristics::record_cycle_end() {
183   _last_cycle_end = os::elapsedTime();
184 }
185 
186 bool ShenandoahHeuristics::should_start_gc() {
187   // Perform GC to cleanup metaspace
188   if (has_metaspace_oom()) {
189     // Some of vmTestbase/metaspace tests depend on following line to count GC cycles
190     log_info(gc)("Trigger: %s", GCCause::to_string(GCCause::_metadata_GC_threshold));
191     return true;
192   }
193 
194   if (ShenandoahGuaranteedGCInterval > 0) {
195     double last_time_ms = (os::elapsedTime() - _last_cycle_end) * 1000;
196     if (last_time_ms > ShenandoahGuaranteedGCInterval) {
197       log_info(gc)("Trigger: Time since last GC (%.0f ms) is larger than guaranteed interval (" UINTX_FORMAT " ms)",
198                    last_time_ms, ShenandoahGuaranteedGCInterval);
199       return true;
200     }
201   }
202 
203   return false;
204 }
205 
206 bool ShenandoahHeuristics::should_degenerate_cycle() {
207   return _degenerated_cycles_in_a_row <= ShenandoahFullGCThreshold;
208 }
209 
210 void ShenandoahHeuristics::adjust_penalty(intx step) {
211   assert(0 <= _gc_time_penalties && _gc_time_penalties <= 100,
212           "In range before adjustment: " INTX_FORMAT, _gc_time_penalties);
213 
214   intx new_val = _gc_time_penalties + step;
215   if (new_val < 0) {
216     new_val = 0;
217   }
218   if (new_val > 100) {
219     new_val = 100;
220   }
221   _gc_time_penalties = new_val;
222 
223   assert(0 <= _gc_time_penalties && _gc_time_penalties <= 100,
224           "In range after adjustment: " INTX_FORMAT, _gc_time_penalties);
225 }
226 
227 void ShenandoahHeuristics::record_success_concurrent() {
228   _degenerated_cycles_in_a_row = 0;
229   _successful_cycles_in_a_row++;
230 
231   _gc_time_history->add(time_since_last_gc());
232   _gc_times_learned++;
233 
234   adjust_penalty(Concurrent_Adjust);




235 }
236 
237 void ShenandoahHeuristics::record_success_degenerated() {
238   _degenerated_cycles_in_a_row++;
239   _successful_cycles_in_a_row = 0;
240 
241   adjust_penalty(Degenerated_Penalty);
242 }
243 
244 void ShenandoahHeuristics::record_success_full() {
245   _degenerated_cycles_in_a_row = 0;
246   _successful_cycles_in_a_row++;
247 
248   adjust_penalty(Full_Penalty);
249 }
250 
251 void ShenandoahHeuristics::record_allocation_failure_gc() {
252   // Do nothing.
253 }
254 

269   if (!ClassUnloadingWithConcurrentMark) return false;
270   if (ShenandoahUnloadClassesFrequency == 0) return false;
271   return true;
272 }
273 
274 bool ShenandoahHeuristics::should_unload_classes() {
275   if (!can_unload_classes_normal()) return false;
276   if (has_metaspace_oom()) return true;
277   size_t cycle = ShenandoahHeap::heap()->shenandoah_policy()->cycle_counter();
278   // Unload classes every Nth GC cycle.
279   // This should not happen in the same cycle as process_references to amortize costs.
280   // Offsetting by one is enough to break the rendezvous when periods are equal.
281   // When periods are not equal, offsetting by one is just as good as any other guess.
282   return (cycle + 1) % ShenandoahUnloadClassesFrequency == 0;
283 }
284 
285 void ShenandoahHeuristics::initialize() {
286   // Nothing to do by default.
287 }
288 
289 double ShenandoahHeuristics::time_since_last_gc() const {
290   return os::elapsedTime() - _cycle_start;
291 }

  1 /*
  2  * Copyright (c) 2018, 2020, Red Hat, Inc. All rights reserved.
  3  * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
  4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  5  *
  6  * This code is free software; you can redistribute it and/or modify it
  7  * under the terms of the GNU General Public License version 2 only, as
  8  * published by the Free Software Foundation.
  9  *
 10  * This code is distributed in the hope that it will be useful, but WITHOUT
 11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 13  * version 2 for more details (a copy is included in the LICENSE file that
 14  * accompanied this code).
 15  *
 16  * You should have received a copy of the GNU General Public License version
 17  * 2 along with this work; if not, write to the Free Software Foundation,
 18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 19  *
 20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 21  * or visit www.oracle.com if you need additional information or have any
 22  * questions.
 23  *
 24  */
 25 
 26 #include "precompiled.hpp"
 27 #include "gc/shared/gcCause.hpp"

 28 #include "gc/shenandoah/shenandoahCollectorPolicy.hpp"

 29 #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp"
 30 #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp"
 31 #include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp"
 32 #include "logging/log.hpp"
 33 #include "logging/logTag.hpp"
 34 #include "runtime/globals_extension.hpp"
 35 #include "utilities/quickSort.hpp"
 36 
 37 // sort by decreasing garbage (so most garbage comes first)
 38 int ShenandoahHeuristics::compare_by_garbage(RegionData a, RegionData b) {
 39   if (a._u._garbage > b._u._garbage)
 40     return -1;
 41   else if (a._u._garbage < b._u._garbage)
 42     return 1;
 43   else return 0;
 44 }
 45 
 46 ShenandoahHeuristics::ShenandoahHeuristics(ShenandoahSpaceInfo* space_info) :
 47   _space_info(space_info),
 48   _region_data(nullptr),
 49   _degenerated_cycles_in_a_row(0),
 50   _successful_cycles_in_a_row(0),
 51   _guaranteed_gc_interval(0),
 52   _cycle_start(os::elapsedTime()),
 53   _last_cycle_end(0),
 54   _gc_times_learned(0),
 55   _gc_time_penalties(0),
 56   _gc_cycle_time_history(new TruncatedSeq(Moving_Average_Samples, ShenandoahAdaptiveDecayFactor)),
 57   _metaspace_oom()
 58 {
 59   // No unloading during concurrent mark? Communicate that to heuristics
 60   if (!ClassUnloadingWithConcurrentMark) {
 61     FLAG_SET_DEFAULT(ShenandoahUnloadClassesFrequency, 0);
 62   }
 63 
 64   size_t num_regions = ShenandoahHeap::heap()->num_regions();
 65   assert(num_regions > 0, "Sanity");
 66 
 67   _region_data = NEW_C_HEAP_ARRAY(RegionData, num_regions, mtGC);
 68 }
 69 
 70 ShenandoahHeuristics::~ShenandoahHeuristics() {
 71   FREE_C_HEAP_ARRAY(RegionGarbage, _region_data);
 72 }
 73 
 74 void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collection_set) {
 75   assert(collection_set->is_empty(), "Must be empty");
 76 
 77   ShenandoahHeap* heap = ShenandoahHeap::heap();
 78 
 79   // Check all pinned regions have updated status before choosing the collection set.
 80   heap->assert_pinned_region_status();
 81 
 82   // Step 1. Build up the region candidates we care about, rejecting losers and accepting winners right away.
 83 
 84   size_t num_regions = heap->num_regions();
 85 
 86   RegionData* candidates = _region_data;
 87 
 88   size_t cand_idx = 0;
 89 
 90   size_t total_garbage = 0;
 91 
 92   size_t immediate_garbage = 0;
 93   size_t immediate_regions = 0;
 94 
 95   size_t free = 0;

 98   ShenandoahMarkingContext* const ctx = heap->complete_marking_context();
 99 
100   for (size_t i = 0; i < num_regions; i++) {
101     ShenandoahHeapRegion* region = heap->get_region(i);
102 
103     size_t garbage = region->garbage();
104     total_garbage += garbage;
105 
106     if (region->is_empty()) {
107       free_regions++;
108       free += ShenandoahHeapRegion::region_size_bytes();
109     } else if (region->is_regular()) {
110       if (!region->has_live()) {
111         // We can recycle it right away and put it in the free set.
112         immediate_regions++;
113         immediate_garbage += garbage;
114         region->make_trash_immediate();
115       } else {
116         // This is our candidate for later consideration.
117         candidates[cand_idx]._region = region;
118         candidates[cand_idx]._u._garbage = garbage;
119         cand_idx++;
120       }
121     } else if (region->is_humongous_start()) {
122       // Reclaim humongous regions here, and count them as the immediate garbage
123 #ifdef ASSERT
124       bool reg_live = region->has_live();
125       bool bm_live = ctx->is_marked(cast_to_oop(region->bottom()));
126       assert(reg_live == bm_live,
127              "Humongous liveness and marks should agree. Region live: %s; Bitmap live: %s; Region Live Words: " SIZE_FORMAT,
128              BOOL_TO_STR(reg_live), BOOL_TO_STR(bm_live), region->get_live_data_words());
129 #endif
130       if (!region->has_live()) {
131         heap->trash_humongous_region_at(region);
132 
133         // Count only the start. Continuations would be counted on "trash" path
134         immediate_regions++;
135         immediate_garbage += garbage;
136       }
137     } else if (region->is_trash()) {
138       // Count in just trashed collection set, during coalesced CM-with-UR
139       immediate_regions++;
140       immediate_garbage += garbage;
141     }
142   }
143 
144   // Step 2. Look back at garbage statistics, and decide if we want to collect anything,
145   // given the amount of immediately reclaimable garbage. If we do, figure out the collection set.
146 
147   assert (immediate_garbage <= total_garbage,
148           "Cannot have more immediate garbage than total garbage: " SIZE_FORMAT "%s vs " SIZE_FORMAT "%s",
149           byte_size_in_proper_unit(immediate_garbage), proper_unit_for_byte_size(immediate_garbage),
150           byte_size_in_proper_unit(total_garbage),     proper_unit_for_byte_size(total_garbage));
151 
152   size_t immediate_percent = (total_garbage == 0) ? 0 : (immediate_garbage * 100 / total_garbage);
153 
154   if (immediate_percent <= ShenandoahImmediateThreshold) {
155     choose_collection_set_from_regiondata(collection_set, candidates, cand_idx, immediate_garbage + free);
156   } else {
157     // We are going to skip evacuation and update refs because we reclaimed
158     // sufficient amounts of immediate garbage.
159     heap->shenandoah_policy()->record_abbreviated_cycle();
160   }
161 
162   size_t cset_percent = (total_garbage == 0) ? 0 : (collection_set->garbage() * 100 / total_garbage);

163   size_t collectable_garbage = collection_set->garbage() + immediate_garbage;
164   size_t collectable_garbage_percent = (total_garbage == 0) ? 0 : (collectable_garbage * 100 / total_garbage);
165 
166   log_info(gc, ergo)("Collectable Garbage: " SIZE_FORMAT "%s (" SIZE_FORMAT "%%), "
167                      "Immediate: " SIZE_FORMAT "%s (" SIZE_FORMAT "%%), " SIZE_FORMAT " regions, "
168                      "CSet: " SIZE_FORMAT "%s (" SIZE_FORMAT "%%), " SIZE_FORMAT " regions",
169 
170                      byte_size_in_proper_unit(collectable_garbage),
171                      proper_unit_for_byte_size(collectable_garbage),
172                      collectable_garbage_percent,
173 
174                      byte_size_in_proper_unit(immediate_garbage),
175                      proper_unit_for_byte_size(immediate_garbage),
176                      immediate_percent,
177                      immediate_regions,
178 
179                      byte_size_in_proper_unit(collection_set->garbage()),
180                      proper_unit_for_byte_size(collection_set->garbage()),
181                      cset_percent,
182                      collection_set->count());
183 }
184 
185 void ShenandoahHeuristics::record_cycle_start() {
186   _cycle_start = os::elapsedTime();
187 }
188 
189 void ShenandoahHeuristics::record_cycle_end() {
190   _last_cycle_end = os::elapsedTime();
191 }
192 
193 bool ShenandoahHeuristics::should_start_gc() {
194   // Perform GC to cleanup metaspace
195   if (has_metaspace_oom()) {
196     // Some of vmTestbase/metaspace tests depend on following line to count GC cycles
197     log_info(gc)("Trigger: %s", GCCause::to_string(GCCause::_metadata_GC_threshold));
198     return true;
199   }
200 
201   if (_guaranteed_gc_interval > 0) {
202     double last_time_ms = (os::elapsedTime() - _last_cycle_end) * 1000;
203     if (last_time_ms > _guaranteed_gc_interval) {
204       log_info(gc)("Trigger (%s): Time since last GC (%.0f ms) is larger than guaranteed interval (" UINTX_FORMAT " ms)",
205                    _space_info->name(), last_time_ms, _guaranteed_gc_interval);
206       return true;
207     }
208   }
209 
210   return false;
211 }
212 
213 bool ShenandoahHeuristics::should_degenerate_cycle() {
214   return _degenerated_cycles_in_a_row <= ShenandoahFullGCThreshold;
215 }
216 
217 void ShenandoahHeuristics::adjust_penalty(intx step) {
218   assert(0 <= _gc_time_penalties && _gc_time_penalties <= 100,
219          "In range before adjustment: " INTX_FORMAT, _gc_time_penalties);
220 
221   intx new_val = _gc_time_penalties + step;
222   if (new_val < 0) {
223     new_val = 0;
224   }
225   if (new_val > 100) {
226     new_val = 100;
227   }
228   _gc_time_penalties = new_val;
229 
230   assert(0 <= _gc_time_penalties && _gc_time_penalties <= 100,
231          "In range after adjustment: " INTX_FORMAT, _gc_time_penalties);
232 }
233 
234 void ShenandoahHeuristics::record_success_concurrent(bool abbreviated) {
235   _degenerated_cycles_in_a_row = 0;
236   _successful_cycles_in_a_row++;


237   _gc_times_learned++;
238 
239   adjust_penalty(Concurrent_Adjust);
240 
241   if (_gc_times_learned <= ShenandoahLearningSteps || !(abbreviated && ShenandoahAdaptiveIgnoreShortCycles)) {
242     _gc_cycle_time_history->add(elapsed_cycle_time());
243   }
244 }
245 
246 void ShenandoahHeuristics::record_success_degenerated() {
247   _degenerated_cycles_in_a_row++;
248   _successful_cycles_in_a_row = 0;
249 
250   adjust_penalty(Degenerated_Penalty);
251 }
252 
253 void ShenandoahHeuristics::record_success_full() {
254   _degenerated_cycles_in_a_row = 0;
255   _successful_cycles_in_a_row++;
256 
257   adjust_penalty(Full_Penalty);
258 }
259 
260 void ShenandoahHeuristics::record_allocation_failure_gc() {
261   // Do nothing.
262 }
263 

278   if (!ClassUnloadingWithConcurrentMark) return false;
279   if (ShenandoahUnloadClassesFrequency == 0) return false;
280   return true;
281 }
282 
283 bool ShenandoahHeuristics::should_unload_classes() {
284   if (!can_unload_classes_normal()) return false;
285   if (has_metaspace_oom()) return true;
286   size_t cycle = ShenandoahHeap::heap()->shenandoah_policy()->cycle_counter();
287   // Unload classes every Nth GC cycle.
288   // This should not happen in the same cycle as process_references to amortize costs.
289   // Offsetting by one is enough to break the rendezvous when periods are equal.
290   // When periods are not equal, offsetting by one is just as good as any other guess.
291   return (cycle + 1) % ShenandoahUnloadClassesFrequency == 0;
292 }
293 
294 void ShenandoahHeuristics::initialize() {
295   // Nothing to do by default.
296 }
297 
298 double ShenandoahHeuristics::elapsed_cycle_time() const {
299   return os::elapsedTime() - _cycle_start;
300 }
< prev index next >