Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: openjdk/shenandoah
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: d2102347
Choose a base ref
...
head repository: openjdk/shenandoah
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 33eacea7
Choose a head ref
Loading
Showing with 1,078 additions and 486 deletions.
  1. +39 −1 src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp
  2. +95 −49 src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp
  3. +186 −88 src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp
  4. +43 −2 src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp
  5. +0 −1 src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp
  6. +36 −6 src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp
  7. +185 −65 src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp
  8. +28 −20 src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp
  9. +0 −10 src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp
  10. +11 −16 src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp
  11. +13 −0 src/hotspot/share/gc/shenandoah/shenandoahGenerationSizer.cpp
  12. +6 −1 src/hotspot/share/gc/shenandoah/shenandoahGenerationSizer.hpp
  13. +34 −25 src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp
  14. +0 −8 src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.hpp
  15. +130 −105 src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp
  16. +1 −7 src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp
  17. +9 −9 src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp
  18. +3 −22 src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp
  19. +0 −15 src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp
  20. +2 −2 src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp
  21. +13 −2 src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp
  22. +36 −20 src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp
  23. +208 −12 test/hotspot/gtest/gc/shenandoah/test_shenandoahOldHeuristic.cpp
Original file line number Diff line number Diff line change
@@ -170,12 +170,50 @@ void ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollectio
bool doing_promote_in_place = (humongous_regions_promoted + regular_regions_promoted_in_place > 0);
if (doing_promote_in_place || (preselected_candidates > 0) || (immediate_percent <= ShenandoahImmediateThreshold)) {
// Only young collections need to prime the collection set.

bool need_to_finalize_piggyback = false;
size_t evacuated_old_bytes, collected_old_bytes, old_evacuation_reserve, old_evacuation_budget, unfragmented_available,
fragmented_available,excess_fragmented_available;
uint included_old_regions;

if (_generation->is_young()) {
heap->old_generation()->heuristics()->prime_collection_set(collection_set);
heap->old_generation()->heuristics()->initialize_piggyback_evacs(collection_set,
evacuated_old_bytes, collected_old_bytes,
included_old_regions, old_evacuation_reserve,
old_evacuation_budget, unfragmented_available,
fragmented_available, excess_fragmented_available);
need_to_finalize_piggyback =heap->old_generation()->heuristics()->prime_collection_set(collection_set,
evacuated_old_bytes,
collected_old_bytes,
included_old_regions,
old_evacuation_reserve,
old_evacuation_budget,
unfragmented_available,
fragmented_available,
excess_fragmented_available);
}

// Call the subclasses to add young-gen regions into the collection set.
choose_collection_set_from_regiondata(collection_set, candidates, cand_idx, immediate_garbage + free);

if (_generation->is_young()) {
// Especially when young-gen trigger is expedited in order to finish mixed evacuations, there may not be
// enough consolidated garbage to make effective use of young-gen evacuation reserve. If there is still
// young-gen reserve available following selection of the young-gen collection set, see if we can use
// this memory to expand the old-gen evacuation collection set.
need_to_finalize_piggyback |=
heap->old_generation()->heuristics()->top_off_collection_set(collection_set,
evacuated_old_bytes, collected_old_bytes,
included_old_regions, old_evacuation_reserve,
old_evacuation_budget, unfragmented_available,
fragmented_available, excess_fragmented_available);
if (need_to_finalize_piggyback) {
heap->old_generation()->heuristics()->finalize_piggyback_evacs(collection_set,
evacuated_old_bytes, collected_old_bytes,
included_old_regions, old_evacuation_reserve,
old_evacuation_budget, unfragmented_available);
}
}
}

if (collection_set->has_old_regions()) {
Original file line number Diff line number Diff line change
@@ -81,96 +81,142 @@ void ShenandoahGlobalHeuristics::choose_global_collection_set(ShenandoahCollecti
size_t cur_young_garbage) const {
auto heap = ShenandoahGenerationalHeap::heap();
size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes();
size_t capacity = heap->young_generation()->max_capacity();
size_t young_capacity = heap->young_generation()->max_capacity();
size_t old_capacity = heap->old_generation()->max_capacity();
size_t garbage_threshold = region_size_bytes * ShenandoahGarbageThreshold / 100;
size_t ignore_threshold = region_size_bytes * ShenandoahIgnoreGarbageThreshold / 100;
const uint tenuring_threshold = heap->age_census()->tenuring_threshold();

size_t young_evac_reserve = heap->young_generation()->get_evacuation_reserve();
size_t old_evac_reserve = heap->old_generation()->get_evacuation_reserve();
size_t max_young_cset = (size_t) (young_evac_reserve / ShenandoahEvacWaste);
size_t young_cur_cset = 0;
size_t max_old_cset = (size_t) (old_evac_reserve / ShenandoahOldEvacWaste);
size_t old_cur_cset = 0;

// Figure out how many unaffiliated young regions are dedicated to mutator and to evacuator. Allow the young
// collector's unaffiliated regions to be transferred to old-gen if old-gen has more easily reclaimed garbage
// than young-gen. At the end of this cycle, any excess regions remaining in old-gen will be transferred back
// to young. Do not transfer the mutator's unaffiliated regions to old-gen. Those must remain available
// to the mutator as it needs to be able to consume this memory during concurrent GC.

size_t unaffiliated_young_regions = heap->young_generation()->free_unaffiliated_regions();
size_t unaffiliated_young_memory = unaffiliated_young_regions * region_size_bytes;

if (unaffiliated_young_memory > max_young_cset) {
size_t unaffiliated_mutator_memory = unaffiliated_young_memory - max_young_cset;
unaffiliated_young_memory -= unaffiliated_mutator_memory;
unaffiliated_young_regions = unaffiliated_young_memory / region_size_bytes; // round down
unaffiliated_young_memory = unaffiliated_young_regions * region_size_bytes;
size_t unaffiliated_old_regions = heap->old_generation()->free_unaffiliated_regions();
size_t unaffiliated_old_memory = unaffiliated_old_regions * region_size_bytes;

// Figure out how many unaffiliated regions are dedicated to Collector and OldCollector reserves. Let these
// be shuffled between young and old generations in order to expedite evacuation of whichever regions have the
// most garbage, regardless of whether these garbage-first regions reside in young or old generation.
// Excess reserves will be transferred back to the mutator after collection set has been chosen. At the end
// of evacuation, any reserves not consumed by evacuation will also be transferred to the mutator free set.
size_t shared_reserve_regions = 0;
if (young_evac_reserve > unaffiliated_young_memory) {
young_evac_reserve -= unaffiliated_young_memory;
shared_reserve_regions += unaffiliated_young_memory / region_size_bytes;
} else {
size_t delta_regions = young_evac_reserve / region_size_bytes;
shared_reserve_regions += delta_regions;
young_evac_reserve -= delta_regions * region_size_bytes;
}
if (old_evac_reserve > unaffiliated_old_memory) {
old_evac_reserve -= unaffiliated_old_memory;
shared_reserve_regions += unaffiliated_old_memory / region_size_bytes;
} else {
size_t delta_regions = old_evac_reserve / region_size_bytes;
shared_reserve_regions += delta_regions;
old_evac_reserve -= delta_regions * region_size_bytes;
}

// We'll affiliate these unaffiliated regions with either old or young, depending on need.
max_young_cset -= unaffiliated_young_memory;
size_t shared_reserves = shared_reserve_regions * region_size_bytes;
size_t committed_from_shared_reserves = 0;
size_t max_young_cset = (size_t) (young_evac_reserve / ShenandoahEvacWaste);
size_t young_cur_cset = 0;
size_t max_old_cset = (size_t) (old_evac_reserve / ShenandoahOldEvacWaste);
size_t old_cur_cset = 0;

size_t promo_bytes = 0;
size_t old_evac_bytes = 0;
size_t young_evac_bytes = 0;

// Keep track of how many regions we plan to transfer from young to old.
size_t regions_transferred_to_old = 0;
size_t shared_regions_consumed_by_old, shared_regions_consumed_by_young;

size_t free_target = (capacity * ShenandoahMinFreeThreshold) / 100 + max_young_cset;
size_t max_total_cset = (max_young_cset + max_old_cset +
(size_t) (shared_reserve_regions * region_size_bytes) / ShenandoahOldEvacWaste);
size_t free_target = ((young_capacity + old_capacity) * ShenandoahMinFreeThreshold) / 100 + max_total_cset;
size_t min_garbage = (free_target > actual_free) ? (free_target - actual_free) : 0;

log_info(gc, ergo)("Adaptive CSet Selection for GLOBAL. Max Young Evacuation: " SIZE_FORMAT
"%s, Max Old Evacuation: " SIZE_FORMAT "%s, Actual Free: " SIZE_FORMAT "%s.",
"%s, Max Old Evacuation: " SIZE_FORMAT "%s, Discretionary additional evacuation: " SIZE_FORMAT
"%s, Actual Free: " SIZE_FORMAT "%s.",
byte_size_in_proper_unit(max_young_cset), proper_unit_for_byte_size(max_young_cset),
byte_size_in_proper_unit(max_old_cset), proper_unit_for_byte_size(max_old_cset),
byte_size_in_proper_unit(shared_reserves), proper_unit_for_byte_size(shared_reserves),
byte_size_in_proper_unit(actual_free), proper_unit_for_byte_size(actual_free));

size_t cur_garbage = cur_young_garbage;
for (size_t idx = 0; idx < size; idx++) {
ShenandoahHeapRegion* r = data[idx]._region;
if (cset->is_preselected(r->index())) {
fatal("There should be no preselected regions during GLOBAL GC");
continue;
}
bool add_region = false;
size_t region_garbage = r->garbage();
size_t new_garbage = cur_garbage + region_garbage;
bool add_regardless = (region_garbage > ignore_threshold) && (new_garbage < min_garbage);
if (r->is_old() || (r->age() >= tenuring_threshold)) {
size_t new_cset = old_cur_cset + r->get_live_data_bytes();
if ((r->garbage() > garbage_threshold)) {
while ((new_cset > max_old_cset) && (unaffiliated_young_regions > 0)) {
unaffiliated_young_regions--;
regions_transferred_to_old++;
if (add_regardless || (region_garbage > garbage_threshold)) {
size_t live_bytes = r->get_live_data_bytes();
size_t new_cset = old_cur_cset + r->get_live_data_bytes();
// May need multiple reserve regions to evacuate a single region, depending on live data bytes and ShenandoahOldEvacWaste
size_t orig_max_old_cset = max_old_cset;
size_t proposed_old_region_consumption = 0;
while ((new_cset > max_old_cset) && (committed_from_shared_reserves < shared_reserves)) {
committed_from_shared_reserves += region_size_bytes;
proposed_old_region_consumption++;
max_old_cset += region_size_bytes / ShenandoahOldEvacWaste;
}
}
if ((new_cset <= max_old_cset) && (r->garbage() > garbage_threshold)) {
add_region = true;
old_cur_cset = new_cset;
// We already know: add_regardless || region_garbage > garbage_threshold
if (new_cset <= max_old_cset) {
shared_regions_consumed_by_old += proposed_old_region_consumption;
add_region = true;
old_cur_cset = new_cset;
cur_garbage = new_garbage;
if (r->is_old()) {
old_evac_bytes += live_bytes;
} else {
promo_bytes += live_bytes;
}
} else {
// We failed to sufficiently expand old, so unwind proposed expansion
max_old_cset = orig_max_old_cset;
committed_from_shared_reserves -= proposed_old_region_consumption * region_size_bytes;
}
}
} else {
assert(r->is_young() && (r->age() < tenuring_threshold), "DeMorgan's law (assuming r->is_affiliated)");
size_t new_cset = young_cur_cset + r->get_live_data_bytes();
size_t region_garbage = r->garbage();
size_t new_garbage = cur_young_garbage + region_garbage;
bool add_regardless = (region_garbage > ignore_threshold) && (new_garbage < min_garbage);

if (add_regardless || (r->garbage() > garbage_threshold)) {
while ((new_cset > max_young_cset) && (unaffiliated_young_regions > 0)) {
unaffiliated_young_regions--;
if (add_regardless || (region_garbage > garbage_threshold)) {
size_t live_bytes = r->get_live_data_bytes();
size_t new_cset = young_cur_cset + live_bytes;
// May need multiple reserve regions to evacuate a single region, depending on live data bytes and ShenandoahEvacWaste
size_t orig_max_young_cset = max_young_cset;
size_t proposed_young_region_consumption = 0;
while ((new_cset > max_young_cset) && (committed_from_shared_reserves < shared_reserves)) {
committed_from_shared_reserves += region_size_bytes;
proposed_young_region_consumption++;
max_young_cset += region_size_bytes / ShenandoahEvacWaste;
}
}
if ((new_cset <= max_young_cset) && (add_regardless || (region_garbage > garbage_threshold))) {
add_region = true;
young_cur_cset = new_cset;
cur_young_garbage = new_garbage;
// We already know: add_regardless || region_garbage > garbage_threshold
if (new_cset <= max_young_cset) {
add_region = true;
young_cur_cset = new_cset;
cur_garbage = new_garbage;
young_evac_bytes += live_bytes;
} else {
// We failed to sufficiently expand young, so unwind proposed expansion
max_young_cset = orig_max_young_cset;
committed_from_shared_reserves -= proposed_young_region_consumption * region_size_bytes;
}
}
}
if (add_region) {
cset->add_region(r);
}
}

if (regions_transferred_to_old > 0) {
heap->generation_sizer()->force_transfer_to_old(regions_transferred_to_old);
heap->young_generation()->set_evacuation_reserve(young_evac_reserve - regions_transferred_to_old * region_size_bytes);
heap->old_generation()->set_evacuation_reserve(old_evac_reserve + regions_transferred_to_old * region_size_bytes);
}
heap->young_generation()->set_evacuation_reserve((size_t) (young_evac_bytes * ShenandoahEvacWaste));
heap->old_generation()->set_evacuation_reserve((size_t) (old_evac_bytes * ShenandoahOldEvacWaste));
heap->old_generation()->set_promoted_reserve((size_t) (promo_bytes * ShenandoahPromoEvacWaste));
}
Loading