From 410014377c210463d654b841bafbcf36947aa960 Mon Sep 17 00:00:00 2001 From: Kelvin Nilsen Date: Fri, 12 Dec 2025 14:02:35 +0000 Subject: [PATCH] 8373225: GenShen: More adaptive old-generation growth heuristics Reviewed-by: wkemper, ysr --- .../shenandoahGenerationalHeuristics.cpp | 2 +- .../heuristics/shenandoahOldHeuristics.cpp | 49 +++++++++++++++++-- .../heuristics/shenandoahOldHeuristics.hpp | 30 ++++++++++++ .../gc/shenandoah/shenandoahGeneration.cpp | 11 +++-- .../gc/shenandoah/shenandoahGeneration.hpp | 2 +- .../shenandoahGenerationalEvacuationTask.cpp | 5 +- .../shenandoahGenerationalFullGC.cpp | 2 +- .../gc/shenandoah/shenandoahOldGeneration.cpp | 37 +++++++++----- .../gc/shenandoah/shenandoahOldGeneration.hpp | 33 ++++++------- .../gc/shenandoah/shenandoah_globals.hpp | 37 +++++++++----- .../generational/TestOldGrowthTriggers.java | 9 +++- 11 files changed, 158 insertions(+), 59 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp index cab6abc4e6f..b14d72f249b 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp @@ -104,7 +104,7 @@ void ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollectio // Note that for GLOBAL GC, region may be OLD, and OLD regions do not qualify for pre-selection // This region is old enough to be promoted but it was not preselected, either because its garbage is below - // ShenandoahOldGarbageThreshold so it will be promoted in place, or because there is not sufficient room + // old garbage threshold so it will be promoted in place, or because there is not sufficient room // in old gen to hold the evacuated copies of this region's live data. In both cases, we choose not to // place this region into the collection set. if (region->get_top_before_promote() != nullptr) { diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp index 1e4d40cfa46..1f257560bcb 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp @@ -71,7 +71,8 @@ ShenandoahOldHeuristics::ShenandoahOldHeuristics(ShenandoahOldGeneration* genera _growth_trigger(false), _fragmentation_density(0.0), _fragmentation_first_old_region(0), - _fragmentation_last_old_region(0) + _fragmentation_last_old_region(0), + _old_garbage_threshold(ShenandoahOldGarbageThreshold) { } @@ -373,7 +374,8 @@ void ShenandoahOldHeuristics::prepare_for_old_collections() { } } - _old_generation->set_live_bytes_after_last_mark(live_data); + // TODO: subtract from live_data bytes promoted during concurrent GC. + _old_generation->set_live_bytes_at_last_mark(live_data); // Unlike young, we are more interested in efficiently packing OLD-gen than in reclaiming garbage first. We sort by live-data. // Some regular regions may have been promoted in place with no garbage but also with very little live data. When we "compact" @@ -385,7 +387,7 @@ void ShenandoahOldHeuristics::prepare_for_old_collections() { const size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); // The convention is to collect regions that have more than this amount of garbage. - const size_t garbage_threshold = region_size_bytes * ShenandoahOldGarbageThreshold / 100; + const size_t garbage_threshold = region_size_bytes * get_old_garbage_threshold() / 100; // Enlightened interpretation: collect regions that have less than this amount of live. const size_t live_threshold = region_size_bytes - garbage_threshold; @@ -655,6 +657,7 @@ bool ShenandoahOldHeuristics::should_start_gc() { const double percent = percent_of(old_gen_capacity, heap_capacity); log_trigger("Expansion failure, current size: %zu%s which is %.1f%% of total heap size", byte_size_in_proper_unit(old_gen_capacity), proper_unit_for_byte_size(old_gen_capacity), percent); + adjust_old_garbage_threshold(); return true; } @@ -677,6 +680,7 @@ bool ShenandoahOldHeuristics::should_start_gc() { "%zu to %zu (%zu), density: %.1f%%", byte_size_in_proper_unit(fragmented_free), proper_unit_for_byte_size(fragmented_free), first_old_region, last_old_region, span_of_old_regions, density * 100); + adjust_old_garbage_threshold(); return true; } @@ -699,12 +703,13 @@ bool ShenandoahOldHeuristics::should_start_gc() { consecutive_young_cycles); _growth_trigger = false; } else if (current_usage > trigger_threshold) { - const size_t live_at_previous_old = _old_generation->get_live_bytes_after_last_mark(); + const size_t live_at_previous_old = _old_generation->get_live_bytes_at_last_mark(); const double percent_growth = percent_of(current_usage - live_at_previous_old, live_at_previous_old); log_trigger("Old has overgrown, live at end of previous OLD marking: " "%zu%s, current usage: %zu%s, percent growth: %.1f%%", byte_size_in_proper_unit(live_at_previous_old), proper_unit_for_byte_size(live_at_previous_old), byte_size_in_proper_unit(current_usage), proper_unit_for_byte_size(current_usage), percent_growth); + adjust_old_garbage_threshold(); return true; } else { // Mixed evacuations have decreased current_usage such that old-growth trigger is no longer relevant. @@ -713,7 +718,41 @@ bool ShenandoahOldHeuristics::should_start_gc() { } // Otherwise, defer to inherited heuristic for gc trigger. - return this->ShenandoahHeuristics::should_start_gc(); + bool result = this->ShenandoahHeuristics::should_start_gc(); + if (result) { + adjust_old_garbage_threshold(); + } + return result; +} + +void ShenandoahOldHeuristics::adjust_old_garbage_threshold() { + const uintx MinimumOldGarbageThreshold = 10; + const uintx InterventionPercentage = 50; + + const ShenandoahHeap* heap = ShenandoahHeap::heap(); + size_t old_regions_size = _old_generation->used_regions_size(); + size_t soft_max_size = heap->soft_max_capacity(); + uintx percent_used = (uintx) ((old_regions_size * 100) / soft_max_size); + _old_garbage_threshold = ShenandoahOldGarbageThreshold; + if (percent_used > InterventionPercentage) { + uintx severity = percent_used - InterventionPercentage; // ranges from 0 to 50 + if (MinimumOldGarbageThreshold < ShenandoahOldGarbageThreshold) { + uintx adjustment_potential = ShenandoahOldGarbageThreshold - MinimumOldGarbageThreshold; + // With default values: + // if percent_used > 80, garbage_threshold is 10 + // else if percent_used > 65, garbage_threshold is 15 + // else if percent_used > 50, garbage_threshold is 20 + if (severity > 30) { + _old_garbage_threshold = ShenandoahOldGarbageThreshold - adjustment_potential; + } else if (severity > 15) { + _old_garbage_threshold = ShenandoahOldGarbageThreshold - 2 * adjustment_potential / 3; + } else { + _old_garbage_threshold = ShenandoahOldGarbageThreshold - adjustment_potential / 3; + } + log_info(gc)("Adjusting old garbage threshold to %lu because Old Generation used regions represents %lu%% of heap", + _old_garbage_threshold, percent_used); + } + } } void ShenandoahOldHeuristics::record_success_concurrent() { diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp index 8d3fec746ba..288d3d68e56 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp @@ -102,6 +102,17 @@ private: size_t _fragmentation_first_old_region; size_t _fragmentation_last_old_region; + // The value of command-line argument ShenandoahOldGarbageThreshold represents the percent of garbage that must + // be present within an old-generation region before that region is considered a good candidate for inclusion in + // the collection set under normal circumstances. For our purposes, normal circustances are when the memory consumed + // by the old generation is less than 50% of the soft heap capacity. When the old generation grows beyond the 50% + // threshold, we dynamically adjust the old garbage threshold, allowing us to invest in packing the old generation + // more tightly so that more memory can be made available to the more frequent young GC cycles. This variable + // is used in place of ShenandoahOldGarbageThreshold. Under normal circumstances, its value is equal to + // ShenandoahOldGarbageThreshold. When the GC is under duress, this value may be adjusted to a smaller value, + // as scaled according to the severity of duress that we are experiencing. + uintx _old_garbage_threshold; + // Compare by live is used to prioritize compaction of old-gen regions. With old-gen compaction, the goal is // to tightly pack long-lived objects into available regions. In most cases, there has not been an accumulation // of garbage within old-gen regions. The more likely opportunity will be to combine multiple sparsely populated @@ -200,9 +211,28 @@ public: bool is_experimental() override; + // Returns the current value of a dynamically adjusted threshold percentage of garbage above which an old region is + // deemed eligible for evacuation. + inline uintx get_old_garbage_threshold() { return _old_garbage_threshold; } + private: void slide_pinned_regions_to_front(); bool all_candidates_are_pinned(); + + // The normal old_garbage_threshold is specified by ShenandoahOldGarbageThreshold command-line argument, with default + // value 25, denoting that a region that has at least 25% garbage is eligible for evacuation. With default values for + // all command-line arguments, we make the following adjustments: + // 1. If the old generation has grown to consume more than 80% of the soft max capacity, adjust threshold to 10% + // 2. Otherwise, if the old generation has grown to consume more than 65%, adjust threshold to 15% + // 3. Otherwise, if the old generation has grown to consume more than 50%, adjust threshold to 20% + // The effect is to compact the old generation more aggressively as the old generation consumes larger percentages + // of the available heap memory. In these circumstances, we pack the old generation more tightly in order to make + // more memory avaiable to the young generation so that the more frequent young collections can operate more + // efficiently. + // + // If the ShenandoahOldGarbageThreshold is specified on the command line, the effect of adjusting the old garbage + // threshold is scaled linearly. + void adjust_old_garbage_threshold(); }; #endif // SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHOLDHEURISTICS_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp index 55d6033b3bc..b9295654b6f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp @@ -505,10 +505,10 @@ inline void assert_no_in_place_promotions() { #endif } -// Preselect for inclusion into the collection set regions whose age is at or above tenure age which contain more than -// ShenandoahOldGarbageThreshold amounts of garbage. We identify these regions by setting the appropriate entry of -// the collection set's preselected regions array to true. All entries are initialized to false before calling this -// function. +// Preselect for inclusion into the collection set all regions whose age is at or above tenure age and for which the +// garbage percentage exceeds a dynamically adjusted threshold (known as the old-garbage threshold percentage). We +// identify these regions by setting the appropriate entry of the collection set's preselected regions array to true. +// All entries are initialized to false before calling this function. // // During the subsequent selection of the collection set, we give priority to these promotion set candidates. // Without this prioritization, we found that the aged regions tend to be ignored because they typically have @@ -531,7 +531,8 @@ size_t ShenandoahGeneration::select_aged_regions(const size_t old_promotion_rese bool* const candidate_regions_for_promotion_by_copy = heap->collection_set()->preselected_regions(); ShenandoahMarkingContext* const ctx = heap->marking_context(); - const size_t old_garbage_threshold = (ShenandoahHeapRegion::region_size_bytes() * ShenandoahOldGarbageThreshold) / 100; + const size_t old_garbage_threshold = + (ShenandoahHeapRegion::region_size_bytes() * heap->old_generation()->heuristics()->get_old_garbage_threshold()) / 100; const size_t pip_used_threshold = (ShenandoahHeapRegion::region_size_bytes() * ShenandoahGenerationalMinPIPUsage) / 100; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp index 424a00f789d..6f393110666 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp @@ -71,7 +71,7 @@ private: // garbage-dense regions, including those that satisfy criteria 1 & 2 below, // and whose live bytes will fit within old_available budget: // Criterion 1. region age >= tenuring threshold - // Criterion 2. region garbage percentage > ShenandoahOldGarbageThreshold + // Criterion 2. region garbage percentage > old garbage threshold // // Identifies regions eligible for promotion in place, // being those of at least tenuring_threshold age that have lower garbage diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp index 4a3faa2c707..de45877994c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp @@ -145,7 +145,7 @@ void ShenandoahGenerationalEvacuationTask::maybe_promote_region(ShenandoahHeapRe // triggers the load-reference barrier (LRB) to copy on reference fetch. // // Aged humongous continuation regions are handled with their start region. If an aged regular region has - // more garbage than ShenandoahOldGarbageThreshold, we'll promote by evacuation. If there is room for evacuation + // more garbage than the old garbage threshold, we'll promote by evacuation. If there is room for evacuation // in this cycle, the region will be in the collection set. If there is not room, the region will be promoted // by evacuation in some future GC cycle. @@ -177,7 +177,8 @@ void ShenandoahGenerationalEvacuationTask::promote_in_place(ShenandoahHeapRegion size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); { - const size_t old_garbage_threshold = (region_size_bytes * ShenandoahOldGarbageThreshold) / 100; + const size_t old_garbage_threshold = + (region_size_bytes * _heap->old_generation()->heuristics()->get_old_garbage_threshold()) / 100; assert(!_heap->is_concurrent_old_mark_in_progress(), "Cannot promote in place during old marking"); assert(region->garbage_before_padded_for_promote() < old_garbage_threshold, "Region %zu has too much garbage for promotion", region->index()); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp index ef913362df3..78672ee10a5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp @@ -83,7 +83,7 @@ void ShenandoahGenerationalFullGC::handle_completion(ShenandoahHeap* heap) { assert_usage_not_more_than_regions_used(young); // Establish baseline for next old-has-grown trigger. - old->set_live_bytes_after_last_mark(old->used()); + old->set_live_bytes_at_last_mark(old->used()); } void ShenandoahGenerationalFullGC::rebuild_remembered_set(ShenandoahHeap* heap) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp index 6a0f986cde5..c7cf013d034 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp @@ -116,11 +116,10 @@ ShenandoahOldGeneration::ShenandoahOldGeneration(uint max_queues) _is_parsable(true), _card_scan(nullptr), _state(WAITING_FOR_BOOTSTRAP), - _growth_before_compaction(INITIAL_GROWTH_BEFORE_COMPACTION), - _min_growth_before_compaction ((ShenandoahMinOldGenGrowthPercent * FRACTIONAL_DENOMINATOR) / 100) + _growth_percent_before_collection(INITIAL_GROWTH_PERCENT_BEFORE_COLLECTION) { assert(type() == ShenandoahGenerationType::OLD, "OO sanity"); - _live_bytes_after_last_mark = ShenandoahHeap::heap()->capacity() * INITIAL_LIVE_FRACTION / FRACTIONAL_DENOMINATOR; + _live_bytes_at_last_mark = (ShenandoahHeap::heap()->soft_max_capacity() * INITIAL_LIVE_PERCENT) / 100; // Always clear references for old generation ref_processor()->set_soft_reference_policy(true); @@ -221,20 +220,20 @@ ShenandoahOldGeneration::configure_plab_for_current_thread(const ShenandoahAlloc } } -size_t ShenandoahOldGeneration::get_live_bytes_after_last_mark() const { - return _live_bytes_after_last_mark; +size_t ShenandoahOldGeneration::get_live_bytes_at_last_mark() const { + return _live_bytes_at_last_mark; } -void ShenandoahOldGeneration::set_live_bytes_after_last_mark(size_t bytes) { +void ShenandoahOldGeneration::set_live_bytes_at_last_mark(size_t bytes) { if (bytes == 0) { // Restart search for best old-gen size to the initial state - _live_bytes_after_last_mark = ShenandoahHeap::heap()->capacity() * INITIAL_LIVE_FRACTION / FRACTIONAL_DENOMINATOR; - _growth_before_compaction = INITIAL_GROWTH_BEFORE_COMPACTION; + _live_bytes_at_last_mark = (ShenandoahHeap::heap()->soft_max_capacity() * INITIAL_LIVE_PERCENT) / 100; + _growth_percent_before_collection = INITIAL_GROWTH_PERCENT_BEFORE_COLLECTION; } else { - _live_bytes_after_last_mark = bytes; - _growth_before_compaction /= 2; - if (_growth_before_compaction < _min_growth_before_compaction) { - _growth_before_compaction = _min_growth_before_compaction; + _live_bytes_at_last_mark = bytes; + _growth_percent_before_collection /= 2; + if (_growth_percent_before_collection < ShenandoahMinOldGenGrowthPercent) { + _growth_percent_before_collection = ShenandoahMinOldGenGrowthPercent; } } } @@ -244,7 +243,19 @@ void ShenandoahOldGeneration::handle_failed_transfer() { } size_t ShenandoahOldGeneration::usage_trigger_threshold() const { - size_t result = _live_bytes_after_last_mark + (_live_bytes_after_last_mark * _growth_before_compaction) / FRACTIONAL_DENOMINATOR; + size_t threshold_by_relative_growth = + _live_bytes_at_last_mark + (_live_bytes_at_last_mark * _growth_percent_before_collection) / 100; + size_t soft_max_capacity = ShenandoahHeap::heap()->soft_max_capacity(); + size_t threshold_by_growth_into_percent_remaining; + if (_live_bytes_at_last_mark < soft_max_capacity) { + threshold_by_growth_into_percent_remaining = (size_t) + (_live_bytes_at_last_mark + ((soft_max_capacity - _live_bytes_at_last_mark) + * ShenandoahMinOldGenGrowthRemainingHeapPercent / 100.0)); + } else { + // we're already consuming more than soft max capacity, so we should start old GC right away. + threshold_by_growth_into_percent_remaining = soft_max_capacity; + } + size_t result = MIN2(threshold_by_relative_growth, threshold_by_growth_into_percent_remaining); return result; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp index 614a1596287..90c1458ac97 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp @@ -287,28 +287,23 @@ public: private: State _state; - static const size_t FRACTIONAL_DENOMINATOR = 65536; - // During initialization of the JVM, we search for the correct old-gen size by initially performing old-gen - // collection when old-gen usage is 50% more (INITIAL_GROWTH_BEFORE_COMPACTION) than the initial old-gen size - // estimate (3.125% of heap). The next old-gen trigger occurs when old-gen grows 25% larger than its live - // memory at the end of the first old-gen collection. Then we trigger again when old-gen grows 12.5% - // more than its live memory at the end of the previous old-gen collection. Thereafter, we trigger each time - // old-gen grows more than 12.5% following the end of its previous old-gen collection. - static const size_t INITIAL_GROWTH_BEFORE_COMPACTION = FRACTIONAL_DENOMINATOR / 2; // 50.0% + // collection when old-gen usage is 50% more (INITIAL_GROWTH_PERCENT_BEFORE_COLLECTION) than the initial old-gen size + // estimate (16% of heap). With each successive old-gen collection, we divide the growth trigger by two, but + // never use a growth trigger smaller than ShenandoahMinOldGenGrowthPercent. + static const size_t INITIAL_GROWTH_PERCENT_BEFORE_COLLECTION = 50; - // INITIAL_LIVE_FRACTION represents the initial guess of how large old-gen should be. We estimate that old-gen - // needs to consume 6.25% of the total heap size. And we "pretend" that we start out with this amount of live + // INITIAL_LIVE_PERCENT represents the initial guess of how large old-gen should be. We estimate that old gen + // needs to consume 16% of the total heap size. And we "pretend" that we start out with this amount of live // old-gen memory. The first old-collection trigger will occur when old-gen occupies 50% more than this initial - // approximation of the old-gen memory requirement, in other words when old-gen usage is 150% of 6.25%, which - // is 9.375% of the total heap size. - static const uint16_t INITIAL_LIVE_FRACTION = FRACTIONAL_DENOMINATOR / 16; // 6.25% + // approximation of the old-gen memory requirement, in other words when old-gen usage is 150% of 16%, which + // is 24% of the heap size. + static const size_t INITIAL_LIVE_PERCENT = 16; - size_t _live_bytes_after_last_mark; + size_t _live_bytes_at_last_mark; - // How much growth in usage before we trigger old collection, per FRACTIONAL_DENOMINATOR (65_536) - size_t _growth_before_compaction; - const size_t _min_growth_before_compaction; // Default is 12.5% + // How much growth in usage before we trigger old collection as a percent of soft_max_capacity + size_t _growth_percent_before_collection; void validate_transition(State new_state) NOT_DEBUG_RETURN; @@ -323,8 +318,8 @@ public: void transition_to(State new_state); - size_t get_live_bytes_after_last_mark() const; - void set_live_bytes_after_last_mark(size_t new_live); + size_t get_live_bytes_at_last_mark() const; + void set_live_bytes_at_last_mark(size_t new_live); size_t usage_trigger_threshold() const; diff --git a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp index 8bd59beb93b..96ebf23bbb5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp @@ -59,15 +59,29 @@ "fail, resulting in stop-the-world full GCs.") \ range(0,100) \ \ - product(double, ShenandoahMinOldGenGrowthPercent, 12.5, EXPERIMENTAL, \ + product(double, ShenandoahMinOldGenGrowthPercent, 50, EXPERIMENTAL, \ "(Generational mode only) If the usage within old generation " \ "has grown by at least this percent of its live memory size " \ - "at completion of the most recent old-generation marking " \ - "effort, heuristics may trigger the start of a new old-gen " \ - "collection.") \ + "at the start of the previous old-generation marking effort, " \ + "heuristics may trigger the start of a new old-gen collection.") \ range(0.0,100.0) \ \ - product(uintx, ShenandoahIgnoreOldGrowthBelowPercentage,10, EXPERIMENTAL, \ + product(double, ShenandoahMinOldGenGrowthRemainingHeapPercent, \ + 35, EXPERIMENTAL, \ + "(Generational mode only) If the usage within old generation " \ + "has grown to exceed this percent of the remaining heap that " \ + "was not marked live within the old generation at the time " \ + "of the last old-generation marking effort, heuristics may " \ + "trigger the start of a new old-gen collection. Setting " \ + "this value to a smaller value may cause back-to-back old " \ + "generation marking triggers, since the typical memory used " \ + "by the old generation is about 30% larger than the live " \ + "memory contained within the old generation (because default " \ + "value of ShenandoahOldGarbageThreshold is 25.") \ + range(0.0,100.0) \ + \ + product(uintx, ShenandoahIgnoreOldGrowthBelowPercentage, \ + 40, EXPERIMENTAL, \ "(Generational mode only) If the total usage of the old " \ "generation is smaller than this percent, we do not trigger " \ "old gen collections even if old has grown, except when " \ @@ -77,12 +91,13 @@ range(0,100) \ \ product(uintx, ShenandoahDoNotIgnoreGrowthAfterYoungCycles, \ - 50, EXPERIMENTAL, \ - "(Generational mode only) Even if the usage of old generation " \ - "is below ShenandoahIgnoreOldGrowthBelowPercentage, " \ - "trigger an old-generation mark if old has grown and this " \ - "many consecutive young-gen collections have been " \ - "completed following the preceding old-gen collection.") \ + 100, EXPERIMENTAL, \ + "(Generational mode only) Trigger an old-generation mark " \ + "if old has grown and this many consecutive young-gen " \ + "collections have been completed following the preceding " \ + "old-gen collection. We perform this old-generation mark " \ + "evvort even if the usage of old generation is below " \ + "ShenandoahIgnoreOldGrowthBelowPercentage.") \ \ product(bool, ShenandoahGenerationalAdaptiveTenuring, true, EXPERIMENTAL, \ "(Generational mode only) Dynamically adapt tenuring age.") \ diff --git a/test/hotspot/jtreg/gc/shenandoah/generational/TestOldGrowthTriggers.java b/test/hotspot/jtreg/gc/shenandoah/generational/TestOldGrowthTriggers.java index 5182d4a9ea3..a66b9161c7e 100644 --- a/test/hotspot/jtreg/gc/shenandoah/generational/TestOldGrowthTriggers.java +++ b/test/hotspot/jtreg/gc/shenandoah/generational/TestOldGrowthTriggers.java @@ -99,8 +99,12 @@ public class TestOldGrowthTriggers { "-XX:+UnlockExperimentalVMOptions", "-XX:+UseShenandoahGC", "-XX:ShenandoahGCMode=generational", + "-XX:ShenandoahMinOldGenGrowthPercent=12.5", + "-XX:ShenandoahIgnoreOldGrowthBelowPercentage=10", + "-XX:ShenandoahMinOldGenGrowthRemainingHeapPercent=100", "-XX:ShenandoahGuaranteedYoungGCInterval=0", - "-XX:ShenandoahGuaranteedOldGCInterval=0" + "-XX:ShenandoahGuaranteedOldGCInterval=0", + "-XX:-UseCompactObjectHeaders" ); testOld("-Xlog:gc", @@ -110,6 +114,9 @@ public class TestOldGrowthTriggers { "-XX:+UnlockExperimentalVMOptions", "-XX:+UseShenandoahGC", "-XX:ShenandoahGCMode=generational", + "-XX:ShenandoahMinOldGenGrowthPercent=12.5", + "-XX:ShenandoahIgnoreOldGrowthBelowPercentage=10", + "-XX:ShenandoahMinOldGenGrowthRemainingHeapPercent=100", "-XX:ShenandoahGuaranteedYoungGCInterval=0", "-XX:ShenandoahGuaranteedOldGCInterval=0", "-XX:+UseCompactObjectHeaders"