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() :
46 _region_data(nullptr),
47 _degenerated_cycles_in_a_row(0),
48 _successful_cycles_in_a_row(0),
49 _cycle_start(os::elapsedTime()),
50 _last_cycle_end(0),
51 _gc_times_learned(0),
52 _gc_time_penalties(0),
53 _gc_time_history(new TruncatedSeq(10, ShenandoahAdaptiveDecayFactor)),
54 _metaspace_oom()
55 {
56 // No unloading during concurrent mark? Communicate that to heuristics
57 if (!ClassUnloadingWithConcurrentMark) {
58 FLAG_SET_DEFAULT(ShenandoahUnloadClassesFrequency, 0);
59 }
60
61 size_t num_regions = ShenandoahHeap::heap()->num_regions();
62 assert(num_regions > 0, "Sanity");
63
64 _region_data = NEW_C_HEAP_ARRAY(RegionData, num_regions, mtGC);
65 }
66
67 ShenandoahHeuristics::~ShenandoahHeuristics() {
68 FREE_C_HEAP_ARRAY(RegionGarbage, _region_data);
69 }
70
71 void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collection_set) {
72 assert(collection_set->count() == 0, "Must be empty");
73
74 ShenandoahHeap* heap = ShenandoahHeap::heap();
75
76 // Check all pinned regions have updated status before choosing the collection set.
77 heap->assert_pinned_region_status();
78
79 // Step 1. Build up the region candidates we care about, rejecting losers and accepting winners right away.
80
81 size_t num_regions = heap->num_regions();
82
83 RegionData* candidates = _region_data;
84
85 size_t cand_idx = 0;
86
87 size_t total_garbage = 0;
88
89 size_t immediate_garbage = 0;
90 size_t immediate_regions = 0;
91
92 size_t free = 0;
93 size_t free_regions = 0;
94
95 ShenandoahMarkingContext* const ctx = heap->complete_marking_context();
96
97 for (size_t i = 0; i < num_regions; i++) {
98 ShenandoahHeapRegion* region = heap->get_region(i);
99
100 size_t garbage = region->garbage();
101 total_garbage += garbage;
102
103 if (region->is_empty()) {
104 free_regions++;
105 free += ShenandoahHeapRegion::region_size_bytes();
106 } else if (region->is_regular()) {
107 if (!region->has_live()) {
108 // We can recycle it right away and put it in the free set.
109 immediate_regions++;
110 immediate_garbage += garbage;
111 region->make_trash_immediate();
112 } else {
113 // This is our candidate for later consideration.
114 candidates[cand_idx]._region = region;
115 candidates[cand_idx]._garbage = garbage;
116 cand_idx++;
117 }
118 } else if (region->is_humongous_start()) {
119 // Reclaim humongous regions here, and count them as the immediate garbage
120 #ifdef ASSERT
121 bool reg_live = region->has_live();
122 bool bm_live = ctx->is_marked(cast_to_oop(region->bottom()));
123 assert(reg_live == bm_live,
124 "Humongous liveness and marks should agree. Region live: %s; Bitmap live: %s; Region Live Words: " SIZE_FORMAT,
125 BOOL_TO_STR(reg_live), BOOL_TO_STR(bm_live), region->get_live_data_words());
126 #endif
127 if (!region->has_live()) {
128 heap->trash_humongous_region_at(region);
129
130 // Count only the start. Continuations would be counted on "trash" path
131 immediate_regions++;
132 immediate_garbage += garbage;
133 }
134 } else if (region->is_trash()) {
135 // Count in just trashed collection set, during coalesced CM-with-UR
136 immediate_regions++;
137 immediate_garbage += garbage;
138 }
139 }
140
141 // Step 2. Look back at garbage statistics, and decide if we want to collect anything,
142 // given the amount of immediately reclaimable garbage. If we do, figure out the collection set.
143
144 assert (immediate_garbage <= total_garbage,
145 "Cannot have more immediate garbage than total garbage: " SIZE_FORMAT "%s vs " SIZE_FORMAT "%s",
146 byte_size_in_proper_unit(immediate_garbage), proper_unit_for_byte_size(immediate_garbage),
147 byte_size_in_proper_unit(total_garbage), proper_unit_for_byte_size(total_garbage));
148
149 size_t immediate_percent = (total_garbage == 0) ? 0 : (immediate_garbage * 100 / total_garbage);
150
151 if (immediate_percent <= ShenandoahImmediateThreshold) {
152 choose_collection_set_from_regiondata(collection_set, candidates, cand_idx, immediate_garbage + free);
153 }
154
155 size_t cset_percent = (total_garbage == 0) ? 0 : (collection_set->garbage() * 100 / total_garbage);
156
157 size_t collectable_garbage = collection_set->garbage() + immediate_garbage;
158 size_t collectable_garbage_percent = (total_garbage == 0) ? 0 : (collectable_garbage * 100 / total_garbage);
159
160 log_info(gc, ergo)("Collectable Garbage: " SIZE_FORMAT "%s (" SIZE_FORMAT "%%), "
161 "Immediate: " SIZE_FORMAT "%s (" SIZE_FORMAT "%%), "
162 "CSet: " SIZE_FORMAT "%s (" SIZE_FORMAT "%%)",
163
164 byte_size_in_proper_unit(collectable_garbage),
165 proper_unit_for_byte_size(collectable_garbage),
166 collectable_garbage_percent,
167
168 byte_size_in_proper_unit(immediate_garbage),
169 proper_unit_for_byte_size(immediate_garbage),
170 immediate_percent,
171
172 byte_size_in_proper_unit(collection_set->garbage()),
173 proper_unit_for_byte_size(collection_set->garbage()),
174 cset_percent);
175 }
176
177 void ShenandoahHeuristics::record_cycle_start() {
178 _cycle_start = os::elapsedTime();
179 }
180
181 void ShenandoahHeuristics::record_cycle_end() {
182 _last_cycle_end = os::elapsedTime();
183 }
184
185 bool ShenandoahHeuristics::should_start_gc() {
186 // Perform GC to cleanup metaspace
187 if (has_metaspace_oom()) {
188 // Some of vmTestbase/metaspace tests depend on following line to count GC cycles
189 log_info(gc)("Trigger: %s", GCCause::to_string(GCCause::_metadata_GC_threshold));
190 return true;
191 }
192
193 if (ShenandoahGuaranteedGCInterval > 0) {
194 double last_time_ms = (os::elapsedTime() - _last_cycle_end) * 1000;
195 if (last_time_ms > ShenandoahGuaranteedGCInterval) {
196 log_info(gc)("Trigger: Time since last GC (%.0f ms) is larger than guaranteed interval (" UINTX_FORMAT " ms)",
197 last_time_ms, ShenandoahGuaranteedGCInterval);
198 return true;
199 }
200 }
201
202 return false;
203 }
204
205 bool ShenandoahHeuristics::should_degenerate_cycle() {
206 return _degenerated_cycles_in_a_row <= ShenandoahFullGCThreshold;
207 }
208
209 void ShenandoahHeuristics::adjust_penalty(intx step) {
210 assert(0 <= _gc_time_penalties && _gc_time_penalties <= 100,
211 "In range before adjustment: " INTX_FORMAT, _gc_time_penalties);
212
213 intx new_val = _gc_time_penalties + step;
214 if (new_val < 0) {
215 new_val = 0;
216 }
217 if (new_val > 100) {
218 new_val = 100;
219 }
220 _gc_time_penalties = new_val;
221
222 assert(0 <= _gc_time_penalties && _gc_time_penalties <= 100,
223 "In range after adjustment: " INTX_FORMAT, _gc_time_penalties);
224 }
225
226 void ShenandoahHeuristics::record_success_concurrent() {
227 _degenerated_cycles_in_a_row = 0;
228 _successful_cycles_in_a_row++;
229
230 _gc_time_history->add(time_since_last_gc());
231 _gc_times_learned++;
232
233 adjust_penalty(Concurrent_Adjust);
234 }
235
236 void ShenandoahHeuristics::record_success_degenerated() {
237 _degenerated_cycles_in_a_row++;
238 _successful_cycles_in_a_row = 0;
239
240 adjust_penalty(Degenerated_Penalty);
241 }
242
243 void ShenandoahHeuristics::record_success_full() {
244 _degenerated_cycles_in_a_row = 0;
245 _successful_cycles_in_a_row++;
246
247 adjust_penalty(Full_Penalty);
248 }
249
250 void ShenandoahHeuristics::record_allocation_failure_gc() {
251 // Do nothing.
252 }
253
254 void ShenandoahHeuristics::record_requested_gc() {
255 // Assume users call System.gc() when external state changes significantly,
256 // which forces us to re-learn the GC timings and allocation rates.
257 _gc_times_learned = 0;
258 }
259
260 bool ShenandoahHeuristics::can_unload_classes() {
261 if (!ClassUnloading) return false;
262 return true;
263 }
264
265 bool ShenandoahHeuristics::can_unload_classes_normal() {
266 if (!can_unload_classes()) return false;
267 if (has_metaspace_oom()) return true;
268 if (!ClassUnloadingWithConcurrentMark) return false;
269 if (ShenandoahUnloadClassesFrequency == 0) return false;
270 return true;
271 }
272
273 bool ShenandoahHeuristics::should_unload_classes() {
274 if (!can_unload_classes_normal()) return false;
275 if (has_metaspace_oom()) return true;
276 size_t cycle = ShenandoahHeap::heap()->shenandoah_policy()->cycle_counter();
277 // Unload classes every Nth GC cycle.
278 // This should not happen in the same cycle as process_references to amortize costs.
279 // Offsetting by one is enough to break the rendezvous when periods are equal.
280 // When periods are not equal, offsetting by one is just as good as any other guess.
281 return (cycle + 1) % ShenandoahUnloadClassesFrequency == 0;
282 }
283
284 void ShenandoahHeuristics::initialize() {
285 // Nothing to do by default.
286 }
287
288 double ShenandoahHeuristics::time_since_last_gc() const {
289 return os::elapsedTime() - _cycle_start;
290 }
|
1 /*
2 * Copyright (c) 2018, 2021, 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/shenandoahAllocRequest.hpp"
28 #include "gc/shenandoah/shenandoahCollectionSet.inline.hpp"
29 #include "gc/shenandoah/shenandoahCollectorPolicy.hpp"
30 #include "gc/shenandoah/shenandoahGeneration.hpp"
31 #include "gc/shenandoah/shenandoahHeap.inline.hpp"
32 #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp"
33 #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp"
34 #include "gc/shenandoah/shenandoahOldGeneration.hpp"
35 #include "gc/shenandoah/shenandoahYoungGeneration.hpp"
36 #include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp"
37 #include "gc/shenandoah/mode/shenandoahMode.hpp"
38 #include "logging/log.hpp"
39 #include "logging/logTag.hpp"
40 #include "runtime/globals_extension.hpp"
41
42 int ShenandoahHeuristics::compare_by_garbage(RegionData a, RegionData b) {
43 if (a._garbage > b._garbage)
44 return -1;
45 else if (a._garbage < b._garbage)
46 return 1;
47 else return 0;
48 }
49
50 ShenandoahHeuristics::ShenandoahHeuristics(ShenandoahGeneration* generation) :
51 _generation(generation),
52 _region_data(nullptr),
53 _degenerated_cycles_in_a_row(0),
54 _successful_cycles_in_a_row(0),
55 _guaranteed_gc_interval(0),
56 _cycle_start(os::elapsedTime()),
57 _last_cycle_end(0),
58 _gc_times_learned(0),
59 _gc_time_penalties(0),
60 _gc_cycle_time_history(new TruncatedSeq(Moving_Average_Samples, ShenandoahAdaptiveDecayFactor)),
61 _metaspace_oom()
62 {
63 // No unloading during concurrent mark? Communicate that to heuristics
64 if (!ClassUnloadingWithConcurrentMark) {
65 FLAG_SET_DEFAULT(ShenandoahUnloadClassesFrequency, 0);
66 }
67
68 size_t num_regions = ShenandoahHeap::heap()->num_regions();
69 assert(num_regions > 0, "Sanity");
70
71 _region_data = NEW_C_HEAP_ARRAY(RegionData, num_regions, mtGC);
72 }
73
74 ShenandoahHeuristics::~ShenandoahHeuristics() {
75 FREE_C_HEAP_ARRAY(RegionGarbage, _region_data);
76 }
77
78 size_t ShenandoahHeuristics::select_aged_regions(size_t old_available, size_t num_regions, bool preselected_regions[]) {
79 ShenandoahHeap* heap = ShenandoahHeap::heap();
80 size_t old_consumed = 0;
81 if (heap->mode()->is_generational()) {
82 for (size_t i = 0; i < num_regions; i++) {
83 ShenandoahHeapRegion* region = heap->get_region(i);
84 if (in_generation(region) && !region->is_empty() && region->is_regular() && (region->age() >= InitialTenuringThreshold)) {
85 size_t promotion_need = (size_t) (region->get_live_data_bytes() * ShenandoahEvacWaste);
86 if (old_consumed + promotion_need < old_available) {
87 old_consumed += promotion_need;
88 preselected_regions[i] = true;
89 }
90 // Note that we keep going even if one region is excluded from selection. Subsequent regions may be selected
91 // if they have smaller live data.
92 }
93 }
94 }
95 return old_consumed;
96 }
97
98 void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collection_set, ShenandoahOldHeuristics* old_heuristics) {
99 ShenandoahHeap* heap = ShenandoahHeap::heap();
100 bool is_generational = heap->mode()->is_generational();
101
102 assert(collection_set->count() == 0, "Must be empty");
103 assert(_generation->generation_mode() != OLD, "Old GC invokes ShenandoahOldHeuristics::choose_collection_set()");
104
105 // Check all pinned regions have updated status before choosing the collection set.
106 heap->assert_pinned_region_status();
107
108 // Step 1. Build up the region candidates we care about, rejecting losers and accepting winners right away.
109
110 size_t num_regions = heap->num_regions();
111
112 RegionData* candidates = _region_data;
113
114 size_t cand_idx = 0;
115
116 size_t total_garbage = 0;
117
118 size_t immediate_garbage = 0;
119 size_t immediate_regions = 0;
120
121 size_t free = 0;
122 size_t free_regions = 0;
123 size_t live_memory = 0;
124
125 for (size_t i = 0; i < num_regions; i++) {
126 ShenandoahHeapRegion* region = heap->get_region(i);
127 if (is_generational && !in_generation(region)) {
128 continue;
129 }
130
131 size_t garbage = region->garbage();
132 total_garbage += garbage;
133 if (region->is_empty()) {
134 free_regions++;
135 free += ShenandoahHeapRegion::region_size_bytes();
136 } else if (region->is_regular()) {
137 if (!region->has_live()) {
138 // We can recycle it right away and put it in the free set.
139 immediate_regions++;
140 immediate_garbage += garbage;
141 region->make_trash_immediate();
142 } else {
143 assert (_generation->generation_mode() != OLD, "OLD is handled elsewhere");
144 live_memory += region->get_live_data_bytes();
145 // This is our candidate for later consideration.
146 candidates[cand_idx]._region = region;
147 if (is_generational && collection_set->is_preselected(i)) {
148 // If region is preselected, we know mode()->is_generational() and region->age() >= InitialTenuringThreshold)
149 garbage = ShenandoahHeapRegion::region_size_bytes();
150 }
151 candidates[cand_idx]._garbage = garbage;
152 cand_idx++;
153 }
154 } else if (region->is_humongous_start()) {
155
156 // Reclaim humongous regions here, and count them as the immediate garbage
157 #ifdef ASSERT
158 bool reg_live = region->has_live();
159 bool bm_live = heap->complete_marking_context()->is_marked(cast_to_oop(region->bottom()));
160 assert(reg_live == bm_live,
161 "Humongous liveness and marks should agree. Region live: %s; Bitmap live: %s; Region Live Words: " SIZE_FORMAT,
162 BOOL_TO_STR(reg_live), BOOL_TO_STR(bm_live), region->get_live_data_words());
163 #endif
164 if (!region->has_live()) {
165 heap->trash_humongous_region_at(region);
166
167 // Count only the start. Continuations would be counted on "trash" path
168 immediate_regions++;
169 immediate_garbage += garbage;
170 } else {
171 live_memory += region->get_live_data_bytes();
172 }
173 } else if (region->is_trash()) {
174 // Count in just trashed collection set, during coalesced CM-with-UR
175 immediate_regions++;
176 immediate_garbage += garbage;
177 } else { // region->is_humongous_cont() and !region->is_trash()
178 live_memory += region->get_live_data_bytes();
179 }
180 }
181
182 // Step 2. Look back at garbage statistics, and decide if we want to collect anything,
183 // given the amount of immediately reclaimable garbage. If we do, figure out the collection set.
184
185 assert (immediate_garbage <= total_garbage,
186 "Cannot have more immediate garbage than total garbage: " SIZE_FORMAT "%s vs " SIZE_FORMAT "%s",
187 byte_size_in_proper_unit(immediate_garbage), proper_unit_for_byte_size(immediate_garbage),
188 byte_size_in_proper_unit(total_garbage), proper_unit_for_byte_size(total_garbage));
189
190 size_t immediate_percent = (total_garbage == 0) ? 0 : (immediate_garbage * 100 / total_garbage);
191 collection_set->set_immediate_trash(immediate_garbage);
192
193 if (immediate_percent <= ShenandoahImmediateThreshold) {
194 if (old_heuristics != nullptr) {
195 old_heuristics->prime_collection_set(collection_set);
196 }
197 // else, this is global collection and doesn't need to prime_collection_set
198
199 // Add young-gen regions into the collection set. This is a virtual call, implemented differently by each
200 // of the heuristics subclasses.
201 choose_collection_set_from_regiondata(collection_set, candidates, cand_idx, immediate_garbage + free);
202 } else {
203 // we're going to skip evacuation and update refs because we reclaimed sufficient amounts of immediate garbage.
204 heap->shenandoah_policy()->record_abbreviated_cycle();
205 }
206
207 if (collection_set->has_old_regions()) {
208 heap->shenandoah_policy()->record_mixed_cycle();
209 }
210
211 size_t cset_percent = (total_garbage == 0) ? 0 : (collection_set->garbage() * 100 / total_garbage);
212 size_t collectable_garbage = collection_set->garbage() + immediate_garbage;
213 size_t collectable_garbage_percent = (total_garbage == 0) ? 0 : (collectable_garbage * 100 / total_garbage);
214
215 log_info(gc, ergo)("Collectable Garbage: " SIZE_FORMAT "%s (" SIZE_FORMAT "%%), "
216 "Immediate: " SIZE_FORMAT "%s (" SIZE_FORMAT "%%) R: " SIZE_FORMAT ", "
217 "CSet: " SIZE_FORMAT "%s (" SIZE_FORMAT "%%) R: " SIZE_FORMAT,
218
219 byte_size_in_proper_unit(collectable_garbage),
220 proper_unit_for_byte_size(collectable_garbage),
221 collectable_garbage_percent,
222
223 byte_size_in_proper_unit(immediate_garbage),
224 proper_unit_for_byte_size(immediate_garbage),
225 immediate_percent,
226 immediate_regions,
227
228 byte_size_in_proper_unit(collection_set->garbage()),
229 proper_unit_for_byte_size(collection_set->garbage()),
230 cset_percent,
231 collection_set->count());
232
233 if (collection_set->garbage() > 0) {
234 size_t young_evac_bytes = collection_set->get_young_bytes_reserved_for_evacuation();
235 size_t promote_evac_bytes = collection_set->get_young_bytes_to_be_promoted();
236 size_t old_evac_bytes = collection_set->get_old_bytes_reserved_for_evacuation();
237 size_t total_evac_bytes = young_evac_bytes + promote_evac_bytes + old_evac_bytes;
238 log_info(gc, ergo)("Evacuation Targets: YOUNG: " SIZE_FORMAT "%s, "
239 "PROMOTE: " SIZE_FORMAT "%s, "
240 "OLD: " SIZE_FORMAT "%s, "
241 "TOTAL: " SIZE_FORMAT "%s",
242 byte_size_in_proper_unit(young_evac_bytes), proper_unit_for_byte_size(young_evac_bytes),
243 byte_size_in_proper_unit(promote_evac_bytes), proper_unit_for_byte_size(promote_evac_bytes),
244 byte_size_in_proper_unit(old_evac_bytes), proper_unit_for_byte_size(old_evac_bytes),
245 byte_size_in_proper_unit(total_evac_bytes), proper_unit_for_byte_size(total_evac_bytes));
246 }
247 }
248
249 void ShenandoahHeuristics::record_cycle_start() {
250 _cycle_start = os::elapsedTime();
251 }
252
253 void ShenandoahHeuristics::record_cycle_end() {
254 _last_cycle_end = os::elapsedTime();
255 }
256
257 bool ShenandoahHeuristics::should_start_gc() {
258 // Perform GC to cleanup metaspace
259 if (has_metaspace_oom()) {
260 // Some of vmTestbase/metaspace tests depend on following line to count GC cycles
261 log_info(gc)("Trigger: %s", GCCause::to_string(GCCause::_metadata_GC_threshold));
262 return true;
263 }
264
265 if (_guaranteed_gc_interval > 0) {
266 double last_time_ms = (os::elapsedTime() - _last_cycle_end) * 1000;
267 if (last_time_ms > _guaranteed_gc_interval) {
268 log_info(gc)("Trigger (%s): Time since last GC (%.0f ms) is larger than guaranteed interval (" UINTX_FORMAT " ms)",
269 _generation->name(), last_time_ms, _guaranteed_gc_interval);
270 return true;
271 }
272 }
273
274 return false;
275 }
276
277 bool ShenandoahHeuristics::should_degenerate_cycle() {
278 return _degenerated_cycles_in_a_row <= ShenandoahFullGCThreshold;
279 }
280
281 void ShenandoahHeuristics::adjust_penalty(intx step) {
282 assert(0 <= _gc_time_penalties && _gc_time_penalties <= 100,
283 "In range before adjustment: " INTX_FORMAT, _gc_time_penalties);
284
285 intx new_val = _gc_time_penalties + step;
286 if (new_val < 0) {
287 new_val = 0;
288 }
289 if (new_val > 100) {
290 new_val = 100;
291 }
292 _gc_time_penalties = new_val;
293
294 assert(0 <= _gc_time_penalties && _gc_time_penalties <= 100,
295 "In range after adjustment: " INTX_FORMAT, _gc_time_penalties);
296 }
297
298 void ShenandoahHeuristics::record_success_concurrent(bool abbreviated) {
299 _degenerated_cycles_in_a_row = 0;
300 _successful_cycles_in_a_row++;
301
302 if (!(abbreviated && ShenandoahAdaptiveIgnoreShortCycles)) {
303 _gc_cycle_time_history->add(elapsed_cycle_time());
304 _gc_times_learned++;
305 }
306
307 adjust_penalty(Concurrent_Adjust);
308 }
309
310 void ShenandoahHeuristics::record_success_degenerated() {
311 _degenerated_cycles_in_a_row++;
312 _successful_cycles_in_a_row = 0;
313
314 adjust_penalty(Degenerated_Penalty);
315 }
316
317 void ShenandoahHeuristics::record_success_full() {
318 _degenerated_cycles_in_a_row = 0;
319 _successful_cycles_in_a_row++;
320
321 adjust_penalty(Full_Penalty);
322 }
323
324 void ShenandoahHeuristics::record_allocation_failure_gc() {
325 // Do nothing.
326 }
327
328 void ShenandoahHeuristics::record_requested_gc() {
329 // Assume users call System.gc() when external state changes significantly,
330 // which forces us to re-learn the GC timings and allocation rates.
331 reset_gc_learning();
332 }
333
334 void ShenandoahHeuristics::reset_gc_learning() {
335 _gc_times_learned = 0;
336 }
337
338 bool ShenandoahHeuristics::can_unload_classes() {
339 if (!ClassUnloading) return false;
340 return true;
341 }
342
343 bool ShenandoahHeuristics::can_unload_classes_normal() {
344 if (!can_unload_classes()) return false;
345 if (has_metaspace_oom()) return true;
346 if (!ClassUnloadingWithConcurrentMark) return false;
347 if (ShenandoahUnloadClassesFrequency == 0) return false;
348 return true;
349 }
350
351 bool ShenandoahHeuristics::should_unload_classes() {
352 if (!can_unload_classes_normal()) return false;
353 if (has_metaspace_oom()) return true;
354 size_t cycle = ShenandoahHeap::heap()->shenandoah_policy()->cycle_counter();
355 // Unload classes every Nth GC cycle.
356 // This should not happen in the same cycle as process_references to amortize costs.
357 // Offsetting by one is enough to break the rendezvous when periods are equal.
358 // When periods are not equal, offsetting by one is just as good as any other guess.
359 return (cycle + 1) % ShenandoahUnloadClassesFrequency == 0;
360 }
361
362 void ShenandoahHeuristics::initialize() {
363 // Nothing to do by default.
364 }
365
366 double ShenandoahHeuristics::elapsed_cycle_time() const {
367 return os::elapsedTime() - _cycle_start;
368 }
369
370 bool ShenandoahHeuristics::in_generation(ShenandoahHeapRegion* region) {
371 return ((_generation->generation_mode() == GLOBAL)
372 || (_generation->generation_mode() == YOUNG && region->affiliation() == YOUNG_GENERATION)
373 || (_generation->generation_mode() == OLD && region->affiliation() == OLD_GENERATION));
374 }
375
376 size_t ShenandoahHeuristics::min_free_threshold() {
377 size_t min_free_threshold =
378 _generation->generation_mode() == GenerationMode::OLD
379 ? ShenandoahOldMinFreeThreshold
380 : ShenandoahMinFreeThreshold;
381 return _generation->soft_max_capacity() / 100 * min_free_threshold;
382 }
|