Skip to content

Commit 33a8085

Browse files
committed
8315479: GenShen: Expand old-gen while selecting collection set during GLOBAL GC
Reviewed-by: wkemper
1 parent c4d0a24 commit 33a8085

File tree

2 files changed

+78
-15
lines changed

2 files changed

+78
-15
lines changed

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

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,39 @@ void ShenandoahGlobalHeuristics::choose_global_collection_set(ShenandoahCollecti
8080
size_t size, size_t actual_free,
8181
size_t cur_young_garbage) const {
8282
ShenandoahHeap* heap = ShenandoahHeap::heap();
83+
size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes();
8384
size_t capacity = heap->young_generation()->max_capacity();
84-
size_t garbage_threshold = ShenandoahHeapRegion::region_size_bytes() * ShenandoahGarbageThreshold / 100;
85-
size_t ignore_threshold = ShenandoahHeapRegion::region_size_bytes() * ShenandoahIgnoreGarbageThreshold / 100;
85+
size_t garbage_threshold = region_size_bytes * ShenandoahGarbageThreshold / 100;
86+
size_t ignore_threshold = region_size_bytes * ShenandoahIgnoreGarbageThreshold / 100;
8687
const uint tenuring_threshold = heap->age_census()->tenuring_threshold();
8788

8889
size_t max_young_cset = (size_t) (heap->get_young_evac_reserve() / ShenandoahEvacWaste);
8990
size_t young_cur_cset = 0;
9091
size_t max_old_cset = (size_t) (heap->get_old_evac_reserve() / ShenandoahOldEvacWaste);
9192
size_t old_cur_cset = 0;
93+
94+
// Figure out how many unaffiliated young regions are dedicated to mutator and to evacuator. Allow the young
95+
// collector's unaffiliated regions to be transferred to old-gen if old-gen has more easily reclaimed garbage
96+
// than young-gen. At the end of this cycle, any excess regions remaining in old-gen will be transferred back
97+
// to young. Do not transfer the mutator's unaffiliated regions to old-gen. Those must remain available
98+
// to the mutator as it needs to be able to consume this memory during concurrent GC.
99+
100+
size_t unaffiliated_young_regions = heap->young_generation()->free_unaffiliated_regions();
101+
size_t unaffiliated_young_memory = unaffiliated_young_regions * region_size_bytes;
102+
103+
if (unaffiliated_young_memory > max_young_cset) {
104+
size_t unaffiliated_mutator_memory = unaffiliated_young_memory - max_young_cset;
105+
unaffiliated_young_memory -= unaffiliated_mutator_memory;
106+
unaffiliated_young_regions = unaffiliated_young_memory / region_size_bytes; // round down
107+
unaffiliated_young_memory = unaffiliated_young_regions * region_size_bytes;
108+
}
109+
110+
// We'll affiliate these unaffiliated regions with either old or young, depending on need.
111+
max_young_cset -= unaffiliated_young_memory;
112+
113+
// Keep track of how many regions we plan to transfer from young to old.
114+
size_t regions_transferred_to_old = 0;
115+
92116
size_t free_target = (capacity * ShenandoahMinFreeThreshold) / 100 + max_young_cset;
93117
size_t min_garbage = (free_target > actual_free) ? (free_target - actual_free) : 0;
94118

@@ -101,31 +125,50 @@ void ShenandoahGlobalHeuristics::choose_global_collection_set(ShenandoahCollecti
101125
for (size_t idx = 0; idx < size; idx++) {
102126
ShenandoahHeapRegion* r = data[idx]._region;
103127
if (cset->is_preselected(r->index())) {
128+
fatal("There should be no preselected regions during GLOBAL GC");
104129
continue;
105130
}
106131
bool add_region = false;
107-
if (r->is_old()) {
132+
if (r->is_old() || (r->age() >= tenuring_threshold)) {
108133
size_t new_cset = old_cur_cset + r->get_live_data_bytes();
134+
if ((r->garbage() > garbage_threshold)) {
135+
while ((new_cset > max_old_cset) && (unaffiliated_young_regions > 0)) {
136+
unaffiliated_young_regions--;
137+
regions_transferred_to_old++;
138+
max_old_cset += region_size_bytes / ShenandoahOldEvacWaste;
139+
}
140+
}
109141
if ((new_cset <= max_old_cset) && (r->garbage() > garbage_threshold)) {
110142
add_region = true;
111143
old_cur_cset = new_cset;
112144
}
113-
} else if (r->age() < tenuring_threshold) {
145+
} else {
146+
assert(r->is_young() && (r->age() < tenuring_threshold), "DeMorgan's law (assuming r->is_affiliated)");
114147
size_t new_cset = young_cur_cset + r->get_live_data_bytes();
115148
size_t region_garbage = r->garbage();
116149
size_t new_garbage = cur_young_garbage + region_garbage;
117150
bool add_regardless = (region_garbage > ignore_threshold) && (new_garbage < min_garbage);
151+
152+
if (add_regardless || (r->garbage() > garbage_threshold)) {
153+
while ((new_cset > max_young_cset) && (unaffiliated_young_regions > 0)) {
154+
unaffiliated_young_regions--;
155+
max_young_cset += region_size_bytes / ShenandoahEvacWaste;
156+
}
157+
}
118158
if ((new_cset <= max_young_cset) && (add_regardless || (region_garbage > garbage_threshold))) {
119159
add_region = true;
120160
young_cur_cset = new_cset;
121161
cur_young_garbage = new_garbage;
122162
}
123163
}
124-
// Note that we do not add aged regions if they were not pre-selected. The reason they were not preselected
125-
// is because there is not sufficient room in old-gen to hold their to-be-promoted live objects.
126-
127164
if (add_region) {
128165
cset->add_region(r);
129166
}
130167
}
168+
169+
if (regions_transferred_to_old > 0) {
170+
heap->generation_sizer()->force_transfer_to_old(regions_transferred_to_old);
171+
heap->set_young_evac_reserve(heap->get_young_evac_reserve() - regions_transferred_to_old * region_size_bytes);
172+
heap->set_old_evac_reserve(heap->get_old_evac_reserve() + regions_transferred_to_old * region_size_bytes);
173+
}
131174
}

src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,23 @@ void ShenandoahGeneration::compute_evacuation_budgets(ShenandoahHeap* heap, bool
298298
// do not add to the update-refs burden of GC.
299299

300300
size_t old_promo_reserve;
301-
if (old_heuristics->unprocessed_old_collection_candidates() > 0) {
301+
if (is_global()) {
302+
// Global GC is typically triggered by user invocation of System.gc(), and typically indicates that there is lots
303+
// of garbage to be reclaimed because we are starting a new phase of execution. Marking for global GC may take
304+
// significantly longer than typical young marking because we must mark through all old objects. To expedite
305+
// evacuation and update-refs, we give emphasis to reclaiming garbage first, wherever that garbage is found.
306+
// Global GC will adjust generation sizes to accommodate the collection set it chooses.
307+
308+
// Set old_promo_reserve to enforce that no regions are preselected for promotion. Such regions typically
309+
// have relatively high memory utilization. We still call select_aged_regions() because this will prepare for
310+
// promotions in place, if relevant.
311+
old_promo_reserve = 0;
312+
313+
// Dedicate all available old memory to old_evacuation reserve. This may be small, because old-gen is only
314+
// expanded based on an existing mixed evacuation workload at the end of the previous GC cycle. We'll expand
315+
// the budget for evacuation of old during GLOBAL cset selection.
316+
old_evacuation_reserve = maximum_old_evacuation_reserve;
317+
} else if (old_heuristics->unprocessed_old_collection_candidates() > 0) {
302318
// We reserved all old-gen memory at end of previous GC to hold anticipated evacuations to old-gen. If this is
303319
// mixed evacuation, reserve all of this memory for compaction of old-gen and do not promote. Prioritize compaction
304320
// over promotion in order to defragment OLD so that it will be better prepared to efficiently receive promoted memory.
@@ -319,21 +335,25 @@ void ShenandoahGeneration::compute_evacuation_budgets(ShenandoahHeap* heap, bool
319335
size_t delta = old_evacuation_reserve - old_free_unfragmented;
320336
old_evacuation_reserve -= delta;
321337

322-
// Let promo consume fragments of old-gen memory.
323-
old_promo_reserve += delta;
338+
// Let promo consume fragments of old-gen memory if not global
339+
if (!is_global()) {
340+
old_promo_reserve += delta;
341+
}
324342
}
325343
collection_set->establish_preselected(preselected_regions);
326344
consumed_by_advance_promotion = select_aged_regions(old_promo_reserve, num_regions, preselected_regions);
327345
assert(consumed_by_advance_promotion <= maximum_old_evacuation_reserve, "Cannot promote more than available old-gen memory");
328-
if (consumed_by_advance_promotion < old_promo_reserve) {
329-
// If we're in a global collection, this memory can be used for old evacuations
330-
old_evacuation_reserve += old_promo_reserve - consumed_by_advance_promotion;
331-
}
346+
347+
// Note that unused old_promo_reserve might not be entirely consumed_by_advance_promotion. Do not transfer this
348+
// to old_evacuation_reserve because this memory is likely very fragmented, and we do not want to increase the likelihood
349+
// of old evacuatino failure.
350+
332351
heap->set_young_evac_reserve(young_evacuation_reserve);
333352
heap->set_old_evac_reserve(old_evacuation_reserve);
334353
heap->set_promoted_reserve(consumed_by_advance_promotion);
335354

336-
// There is no need to expand OLD because all memory used here was set aside at end of previous GC
355+
// There is no need to expand OLD because all memory used here was set aside at end of previous GC, except in the
356+
// case of a GLOBAL gc. During choose_collection_set() of GLOBAL, old will be expanded on demand.
337357
}
338358

339359
// Having chosen the collection set, adjust the budgets for generational mode based on its composition. Note

0 commit comments

Comments
 (0)