RFR: 8374307: Fix deoptimization storm caused by Action_none in GraphKit::uncommon_trap

Quan Anh Mai qamai at openjdk.org
Tue Dec 23 18:44:58 UTC 2025


On Tue, 23 Dec 2025 18:16:33 GMT, Boris Ulasevich <bulasevich at openjdk.org> wrote:

> We observed a deoptimization storm caused by GraphKit::uncommon_trap generator logic. GraphKit::uncommon_trap considers the too_many_recompiles metric. If the threshold is overflowed, it replaces Deoptimization::Action_reinterpret with Deoptimization::Action_none (see code snippet below).
> 
> This replacement changes the uncommon_trap logic: once execution hits a trap, the VM performs deoptimization but does not recompile the method anymore. In an "unlucky" case, when the code part calling this uncommon_trap becomes frequent, a deoptimization storm occurs (thousands of deoptimizations per second) causing a significant performance drop.
> 
> The original problematic method, which triggered repeated recompilations, is a high-performance compressed binary serialization algorithm with heavy use of conditional branches driven by bitmasks. See a standalone [synthetic benchmark](https://bugs.openjdk.org/secure/attachment/118045/UnstableIf.java) to reproduce the issue.
> 
> The issue arises when the method overcomes a global recompilation threshold before stabilizing specific trap counters.
> 
> Current thresholds:
> - Recompilation Limit (too_many_recompiles):
>   Condition: decompile_count() >= (PerMethodRecompilationCutoff / 2) + 1
>   Default: 201 (derived from default PerMethodRecompilationCutoff = 400).
> - Specific Trap Limits (too_many_traps):
>   Checks if the trap count for a specific reason exceeds:
>   PerMethodTrapLimit (Default: 100) - for Reason_unstable_if, Reason_unstable_fused_if, etc.
>   PerMethodSpecTrapLimit (Default: 5000) - for Reason_speculate_class_check, Reason_speculate_null_check, etc.
> 
> With the gived defaults, if the only reason for the method recompilation is unstable_if, the system stabilizes after 100 traps (PerMethodTrapLimit). However, if the method experiences traps and recompilations for different reasons, the total number of recompilations can exceed 200 before hitting the limit for unstable_if traps. This triggers Action_none and causes the deopt storm.
> 
> The proposal is a minimal change in GraphKit::uncommon_trap: apply the same `too_many_recompiles` threshold inside `Parse::path_is_suitable_for_uncommon_trap` - this ensures that on the final recompilation C2 gets a hint not to speculate on untaken branches anymore.
> 
> As an alternative solution, we can revisit GraphKit::uncommon_trap. This "Temporary fix" has persisted in the codebase for 17 years, so it is probably time to change it as well. Any comments are welcome
> 
>   case Deoptimization::Action_reinter...

src/hotspot/share/opto/parse2.cpp line 1621:

> 1619:   return seems_never_taken(prob) &&
> 1620:          // Skip optimization if recompile limit is exceeded to avoid deopts without recompilation.
> 1621:          !C->too_many_recompiles(method(), bci(), Deoptimization::Reason_unstable_if) &&

You can use `Compile::too_many_traps_or_recompile` here.

-------------

PR Review Comment: https://git.openjdk.org/jdk/pull/28966#discussion_r2644002566


More information about the hotspot-compiler-dev mailing list