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

Vladimir Ivanov vlivanov at openjdk.org
Wed Jan 7 19:20:28 UTC 2026


On Wed, 7 Jan 2026 17:28:14 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 we...
>
> Boris Ulasevich has refreshed the contents of this pull request, and previous commits have been removed. The incremental views will show differences compared to the previous content of the PR. The pull request contains one new commit since the last revision:
> 
>   using too_many_traps_or_recompiles. adding DeoptStorm jtreg test

BTW [JDK-6529811](https://bugs.openjdk.org/browse/JDK-6529811) did not introduce the heuristic in `GraphKit::uncommon_trap()`. The code predates OpenJDK. JDK-6529811 mentions an alternative way to fix the pathological behavior: 

5. The Action_none bailout is dangerous. GraphKit::uncommon_trap should bail out to Action_make_not_compilable. That way the log will print an interesting failure event, and performance will degrade into the interpreter, which is faster than the deoptimizer.

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

PR Comment: https://git.openjdk.org/jdk/pull/28966#issuecomment-3720391303


More information about the hotspot-compiler-dev mailing list