RFR: 8357471: GenShen: Share collector reserves between young and old
William Kemper
wkemper at openjdk.org
Wed May 21 18:37:54 UTC 2025
On Wed, 21 May 2025 15:29:09 GMT, Kelvin Nilsen <kdnilsen at openjdk.org> wrote:
> Genshen independently reserves memory to hold evacuations into young and old generations. We have found that under duress, it is sometimes difficult for mixed evacuations to make progress because the reserves in old are too small and we cannot expand old because young is running so frequently that it does not have the excess memory required to justify expansion of old (and shrinking of young).
>
> This PR exploits the fact that the reserves in young are often much larger than young requires to carry out its anticipated next GC cycle. In this case, we can share the young collector reserves with the old generation. This allows much more effective operation of mixed evacuations when GC is running at or near its full capacity.
>
> The following spreadsheet snapshots highlight the benefits of this change. In control with 6G heap size, we perform large numbers of mixed evacuations, but each mixed evacuation has very low productivity (e.g. one region at a time). This causes excessive delays in reclaiming the garbage from old, which is required to shrink old and expand young. This is why we see the large number of unproductive GC cycles, many of which degenerate and a few of which upgrade to full GC. In the experiment with 6G heap size, there are far fewer mixed cycles, but they are each much more productive. The total number of GC cycles decreases significantly.
>
> 
>
> With 7G heap size, the benefits of this PR manifest as a decrease in mixed evacuations, which also allows us to decrease total GC cycles. By more quickly reclaiming old garbage, we are able to more quickly expand young, which decreases the number of young GC cycles. This reduces CPU load. The impact on response times is not as significant as with the 6G heap size. We see slight improvement at p50-p99.9, with slight degradation at p99.99 through p100.
>
> 
>
> At 8G heap size, the GC is not at all stressed. We see approximately the same numbers of GC cycles, slight degradation of response times at p50-p99, slight improvement in response times at p99.9-p100.
>
> 
>
> The command line for these comparisons follows:
>
>
> ~/github/jdk.share-collector-reserves/build/linux-x86_64-server-release/images/jdk/bin/java \
> -XX:+Unlock...
Changes requested by wkemper (Reviewer).
src/hotspot/share/gc/shenandoah/shenandoahGenerationSizer.cpp line 193:
> 191: const size_t new_size = old_gen->max_capacity();
> 192: log_info(gc, ergo)("Forcing transfer of %zu region(s) from %s to %s, yielding increased size: " PROPERFMT,
> 193: regions, young_gen->name(), old_gen->name(), PROPERFMTARGS(new_size));
If this is now really only used for in-place promotions, can we change the log message to indicate the region is being promoted? I think when users see messages about things being "forced" in the log, they start to wonder if young/old sizes need to adjusted.
src/hotspot/share/gc/shenandoah/shenandoahGenerationSizer.cpp line 206:
> 204: old_gen->decrease_capacity(bytes_to_transfer);
> 205: const size_t new_size = young_gen->max_capacity();
> 206: log_info(gc)("Forcing transfer of %zu region(s) from %s to %s, yielding increased size: " PROPERFMT,
Can this be `gc, ergo` like the method to transfer regions to old?
src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp line 298:
> 296: if (copy == nullptr) {
> 297: // If we failed to allocate in LAB, we'll try a shared allocation.
> 298: #ifdef KELVIN_ORIGINAL
This looks like debugging code? Should we back this out?
src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp line 149:
> 147:
> 148: // Transfers surplus old regions to young, or takes regions from young to satisfy old region deficit
> 149: TransferResult balance_generations();
Are we still using this `TransferResult` thing? Seems like we might be able to delete it with this change.
src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp line 1670:
> 1668:
> 1669: if (mode()->is_generational()) {
> 1670: // young-gen heuristics track young, bootstrap, and global GC cycle times
This seems like an unrelated change. Mixing in bootstrap and global gc cycle times is likely to increase the average time and make the heuristics more aggressive.
src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp line 279:
> 277: static size_t setup_sizes(size_t max_heap_size);
> 278:
> 279: inline bool is_recycling() {
Is this used anywhere?
src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp line 376:
> 374: "runs out of memory too early.") \
> 375: \
> 376: product(uintx, ShenandoahOldEvacRatioPercent, 75, EXPERIMENTAL, \
Phew, this is a lot of explanatory text and it reads like the target audience are GC developers. If we are going to expose this as a user configurable option, I think the help text should just explain how the behavior changes as this values goes up or down. Something like:
> Increasing this allows for more old regions in mixed collections. Decreasing this reduces the number of old regions in mixed collections.
The first sentence makes it seem as though this is the percentage of the entire heap to reserve for old evacuations, but the next clarifies that this is the percentage of the collection set.
Question about this sentence:
> A value of 100 allows a mixed evacuation to focus entirely on old-gen memory, allowing no young-gen regions to be collected.
With a setting of 100, would GenShen still "preselect" young regions of tenuring age with sufficient garbage into the collection set?
I also find the name of the option slightly confusing - is it a ratio? or a percentage? Seems like it's really a percentage (though it controls the ratio of reserves used for the collection).
-------------
PR Review: https://git.openjdk.org/jdk/pull/25357#pullrequestreview-2858665038
PR Review Comment: https://git.openjdk.org/jdk/pull/25357#discussion_r2100871366
PR Review Comment: https://git.openjdk.org/jdk/pull/25357#discussion_r2100868280
PR Review Comment: https://git.openjdk.org/jdk/pull/25357#discussion_r2100879212
PR Review Comment: https://git.openjdk.org/jdk/pull/25357#discussion_r2100931112
PR Review Comment: https://git.openjdk.org/jdk/pull/25357#discussion_r2100887452
PR Review Comment: https://git.openjdk.org/jdk/pull/25357#discussion_r2100894053
PR Review Comment: https://git.openjdk.org/jdk/pull/25357#discussion_r2100926884
More information about the hotspot-gc-dev
mailing list