3838
3939using idx_t = ShenandoahSimpleBitMap::idx_t ;
4040
41- typedef struct {
42- ShenandoahHeapRegion* _region;
43- size_t _live_data;
44- } AgedRegionData;
45-
4641static int compare_by_aged_live (AgedRegionData a, AgedRegionData b) {
4742 if (a._live_data < b._live_data )
4843 return -1 ;
@@ -321,21 +316,57 @@ void ShenandoahGenerationalHeuristics::filter_regions(ShenandoahCollectionSet* c
321316 immediate_garbage);
322317}
323318
324- // Select for inclusion into the collection set all regions whose age is at or above tenure age and for which the
325- // garbage percentage exceeds a dynamically adjusted threshold (known as the old-garbage threshold percentage). We
326- // identify these regions by setting the appropriate entry of the collection set's preselected regions array to true.
327- // All entries are initialized to false before calling this function.
319+ void ShenandoahGenerationalHeuristics::add_tenured_regions_to_collection_set (const size_t old_promotion_reserve,
320+ ShenandoahGenerationalHeap *const heap,
321+ size_t candidates, AgedRegionData* sorted_regions) {
322+ size_t old_consumed = 0 ;
323+ if (candidates > 0 ) {
324+ // Sort in increasing order according to live data bytes. Note that
325+ // candidates represents the number of regions that qualify to be promoted
326+ // by evacuation.
327+ QuickSort::sort<AgedRegionData>(sorted_regions, candidates,
328+ compare_by_aged_live);
329+
330+ size_t selected_regions = 0 ;
331+ size_t selected_live = 0 ;
332+ for (size_t i = 0 ; i < candidates; i++) {
333+ ShenandoahHeapRegion *const region = sorted_regions[i]._region ;
334+ const size_t region_live_data = sorted_regions[i]._live_data ;
335+ const size_t promotion_need = (size_t )(region_live_data * ShenandoahPromoEvacWaste);
336+ if (old_consumed + promotion_need > old_promotion_reserve) {
337+ // We rejected the remaining promotable regions from the collection set
338+ // because we have no room to hold their evacuees. We do not need to
339+ // iterate the remaining regions to estimate the amount we expect to
340+ // promote because we know it directly form the census we computed
341+ // during the preceding mark phase.
342+ break ;
343+ }
344+
345+ old_consumed += promotion_need;
346+ heap->collection_set ()->add_region (region);
347+ selected_regions++;
348+ selected_live += region_live_data;
349+ }
350+ log_debug (gc, ergo)( " Preselected %zu regions containing " PROPERFMT " live data,"
351+ " consuming: " PROPERFMT " of budgeted: " PROPERFMT,
352+ selected_regions, PROPERFMTARGS (selected_live),
353+ PROPERFMTARGS (old_consumed), PROPERFMTARGS (old_promotion_reserve));
354+ }
355+ }
356+
357+ // Select for inclusion into the collection set all regions whose age is at or
358+ // above tenure age and for which the
359+ // garbage percentage exceeds a dynamically adjusted threshold (known as the old-garbage threshold percentage).
328360//
329- // During the subsequent selection of the collection set, we give priority to these promotion set candidates.
330361// Without this prioritization, we found that the aged regions tend to be ignored because they typically have
331362// much less garbage and much more live data than the recently allocated "eden" regions. When aged regions are
332363// repeatedly excluded from the collection set, the amount of live memory within the young generation tends to
333364// accumulate and this has the undesirable side effect of causing young-generation collections to require much more
334365// CPU and wall-clock time.
335366//
336367// A second benefit of treating aged regions differently than other regions during collection set selection is
337- // that this allows us to more accurately budget memory to hold the results of evacuation. Memory for evacuation
338- // of aged regions must be reserved in the old generation. Memory for evacuation of all other regions must be
368+ // that this allows us to more accurately budget memory to hold the results of evacuation. Memory for evacuation
369+ // of aged regions must be reserved in the old generation. Memory for evacuation of all other regions must be
339370// reserved in the young generation.
340371size_t ShenandoahGenerationalHeuristics::select_aged_regions (ShenandoahInPlacePromotionPlanner& in_place_promotions,
341372 const size_t old_promotion_reserve) {
@@ -345,7 +376,6 @@ size_t ShenandoahGenerationalHeuristics::select_aged_regions(ShenandoahInPlacePr
345376
346377 auto const heap = ShenandoahGenerationalHeap::heap ();
347378
348- size_t promo_potential = 0 ;
349379 size_t candidates = 0 ;
350380
351381 // Sort the promotion-eligible regions in order of increasing live-data-bytes so that we can first reclaim regions that require
@@ -386,66 +416,28 @@ size_t ShenandoahGenerationalHeuristics::select_aged_regions(ShenandoahInPlacePr
386416 sorted_regions[candidates]._live_data = r->get_live_data_bytes ();
387417 candidates++;
388418 }
389- } else {
390- // We only evacuate & promote objects from regular regions whose garbage() is above old-garbage-threshold.
391- // Objects in tenure-worthy regions with less garbage are promoted in place. These take a different path to
392- // old-gen. Regions excluded from promotion because their garbage content is too low (causing us to anticipate that
393- // the region would be promoted in place) may be eligible for evacuation promotion by the time promotion takes
394- // place during a subsequent GC pass because more garbage is found within the region between now and then. This
395- // should not happen if we are properly adapting the tenure age. The theory behind adaptive tenuring threshold
396- // is to choose the youngest age that demonstrates no "significant" further loss of population since the previous
397- // age. If not this, we expect the tenure age to demonstrate linear population decay for at least two population
398- // samples, whereas we expect to observe exponential population decay for ages younger than the tenure age.
399- //
400- // In the case that certain regions which were anticipated to be promoted in place need to be promoted by
401- // evacuation, it may be the case that there is not sufficient reserve within old-gen to hold evacuation of
402- // these regions. The likely outcome is that these regions will not be selected for evacuation or promotion
403- // in the current cycle and we will anticipate that they will be promoted in the next cycle. This will cause
404- // us to reserve more old-gen memory so that these objects can be promoted in the subsequent cycle.
405- if (heap->is_aging_cycle () && heap->age_census ()->is_tenurable (r->age () + 1 )) {
406- if (r->garbage () >= in_place_promotions.old_garbage_threshold ()) {
407- promo_potential += r->get_live_data_bytes ();
408- }
409- }
410419 }
411- // Note that we keep going even if one region is excluded from selection.
412- // Subsequent regions may be selected if they have smaller live data.
413420 }
414421
415422 in_place_promotions.complete_planning ();
416423
417- // Sort in increasing order according to live data bytes. Note that candidates represents the number of regions
418- // that qualify to be promoted by evacuation.
419- size_t old_consumed = 0 ;
420- if (candidates > 0 ) {
421- size_t selected_regions = 0 ;
422- size_t selected_live = 0 ;
423- QuickSort::sort<AgedRegionData>(sorted_regions, candidates, compare_by_aged_live);
424- for (size_t i = 0 ; i < candidates; i++) {
425- ShenandoahHeapRegion* const region = sorted_regions[i]._region ;
426- const size_t region_live_data = sorted_regions[i]._live_data ;
427- const size_t promotion_need = (size_t ) (region_live_data * ShenandoahPromoEvacWaste);
428- if (old_consumed + promotion_need <= old_promotion_reserve) {
429- old_consumed += promotion_need;
430- heap->collection_set ()->add_region (region);
431- selected_regions++;
432- selected_live += region_live_data;
433- } else {
434- // We rejected this promotable region from the collection set because we had no room to hold its copy.
435- // Add this region to promo potential for next GC.
436- promo_potential += region_live_data;
437- assert (!heap->collection_set ()->is_in (region), " Region %zu shouldn't be in the collection set" , region->index ());
438- }
439- // We keep going even if one region is excluded from selection because we need to accumulate all eligible
440- // regions that are not preselected into promo_potential
441- }
442- log_debug (gc, ergo)(" Preselected %zu regions containing " PROPERFMT " live data,"
443- " consuming: " PROPERFMT " of budgeted: " PROPERFMT,
444- selected_regions, PROPERFMTARGS (selected_live), PROPERFMTARGS (old_consumed), PROPERFMTARGS (old_promotion_reserve));
445- }
424+ add_tenured_regions_to_collection_set (old_promotion_reserve, heap, candidates, sorted_regions);
446425
447- log_info (gc, ergo)(" Promotion potential of aged regions with sufficient garbage: " PROPERFMT, PROPERFMTARGS (promo_potential));
426+ const uint tenuring_threshold = heap->age_census ()->tenuring_threshold ();
427+ const size_t tenurable_this_cycle = heap->age_census ()->get_tenurable_bytes (tenuring_threshold);
428+ const size_t tenurable_next_cycle = heap->age_census ()->get_tenurable_bytes (tenuring_threshold - 1 );
429+ assert (tenurable_next_cycle >= tenurable_this_cycle,
430+ " Tenurable next cycle (" PROPERFMT " ) should include tenurable this cycle (" PROPERFMT " )" ,
431+ PROPERFMTARGS (tenurable_next_cycle), PROPERFMTARGS (tenurable_this_cycle));
432+
433+ const size_t max_promotions = tenurable_this_cycle * ShenandoahPromoEvacWaste;
434+ const size_t old_consumed = MIN2 (max_promotions, old_promotion_reserve);
435+
436+ // Don't include the bytes we expect to promote in this cycle in the next cycle
437+ const size_t promo_potential = (tenurable_next_cycle - tenurable_this_cycle) * ShenandoahPromoEvacWaste;
448438 heap->old_generation ()->set_promotion_potential (promo_potential);
439+ log_info (gc, ergo)(" Promotion potential of aged regions with sufficient garbage: " PROPERFMT, PROPERFMTARGS (promo_potential));
440+
449441 return old_consumed;
450442}
451443
0 commit comments