Skip to content

Add JacksonWarmup to pre-settle JIT#3512

Open
schlosna wants to merge 1 commit into
developfrom
davids/jackson-warmup
Open

Add JacksonWarmup to pre-settle JIT#3512
schlosna wants to merge 1 commit into
developfrom
davids/jackson-warmup

Conversation

@schlosna

@schlosna schlosna commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Before this PR

OpenJDK could encounter pathological C2 JIT deoptimization storm in Jackson BeanPropertyWriter.java:731 leading to ~10x performance degradation and significant increases in CPU utilization.

See https://bugs.openjdk.org/browse/JDK-8330258 & https://bugs.openjdk.org/browse/JDK-8374307

OpenJDK has an open PR openjdk/jdk#28966 that may address this issue by switching from checking too_many_traps to too_many_traps_or_recompiles.

After this PR

==COMMIT_MSG==
Adds a JacksonWarmup type that serializes a representative set of payload shapes through fresh server and dialogue/client ObjectMappers (constructed via the same conjure-java factories used at runtime) so the JIT settles on the bimorphic BeanPropertyWriter.serializeAsField branch (typed vs untyped serializer) with full type-profile coverage before customer traffic perturbs it.

This works around OpenJDK C2 JIT deoptimization storm where uncommon_trap isnonnull triggers continuous deoptimization as seen in https://bugs.openjdk.org/browse/JDK-8330258 &
https://bugs.openjdk.org/browse/JDK-8374307 . The repeated deoptimization can be observed by jdk.Deoptimization JFR events at com.fasterxml.jackson.databind.ser.BeanPropertyWriter if (_typeSerializer == null) site, and then re-enters unstable_if deopt thrash on the serialization path when a perturbing workload arrives.

The warmup payloads interleave monomorphic fields (concrete types) with polymorphic fields (sealed interface + @JsonTypeInfo) in the same object so each iteration exercises both branches, letting C2 record a bimorphic profile during the warmup pass.

Warmup failures are caught and logged rather than aborting as a degraded JIT profile is preferable to refusing to serve traffic.
==COMMIT_MSG==

Possible downsides?

@changelog-app

changelog-app Bot commented Jun 17, 2026

Copy link
Copy Markdown

Generate changelog in changelog/@unreleased

Type (Select exactly one)

  • Feature (Adding new functionality)
  • Improvement (Improving existing functionality)
  • Fix (Fixing an issue with existing functionality)
  • Break (Creating a new major version by breaking public APIs)
  • Deprecation (Removing functionality in a non-breaking way)
  • Migration (Automatically moving data/functionality to a new system)

Description

Adds a JacksonWarmup type that serializes a representative set of payload shapes through fresh server and dialogue/client ObjectMappers (constructed via the same conjure-java factories used at runtime) so the JIT settles on the bimorphic BeanPropertyWriter.serializeAsField branch (typed vs untyped serializer) with full type-profile coverage before customer traffic perturbs it.

This works around OpenJDK C2 JIT deoptimization storm where uncommon_trap isnonnull triggers continuous deoptimization as seen in https://bugs.openjdk.org/browse/JDK-8330258 &
https://bugs.openjdk.org/browse/JDK-8374307 . The repeated deoptimization can be observed by jdk.Deoptimization JFR events at com.fasterxml.jackson.databind.ser.BeanPropertyWriter if (_typeSerializer == null) site, and then re-enters unstable_if deopt thrash on the JSON serialization path when a perturbing workload arrives.

The warmup payloads interleave monomorphic fields (concrete types) with polymorphic fields (sealed interface + @JsonTypeInfo) in the same object so each iteration exercises both branches, letting C2 record a bimorphic profile during the warmup pass.

Warmup failures are caught and logged rather than aborting as a degraded JIT profile is preferable to refusing to serve traffic.

Check the box to generate changelog(s)

  • Generate changelog entry

@schlosna schlosna force-pushed the davids/jackson-warmup branch from 97ad5c3 to e73b09b Compare June 17, 2026 13:52
Adds a JacksonWarmup type that serializes a representative set of
payload shapes through fresh server and dialogue/client ObjectMappers
(constructed via the same conjure-java factories used at runtime) so the
JIT settles on the bimorphic BeanPropertyWriter.serializeAsField branch
(typed vs untyped serializer) with full type-profile coverage before
customer traffic perturbs it.

This works around OpenJDK C2 JIT deoptimization storm where
uncommon_trap isnonnull triggers continuous deoptimization as seen in
https://bugs.openjdk.org/browse/JDK-8330258 &
https://bugs.openjdk.org/browse/JDK-8374307 . The repeated
deoptimization can be observed by `jdk.Deoptimization` JFR events at
`com.fasterxml.jackson.databind.ser.BeanPropertyWriter` `if
(_typeSerializer == null)` site, and then re-enters unstable_if deopt
thrash on the JSON serialization path when a perturbing workload
arrives.

The warmup payloads interleave monomorphic fields (concrete types) with
polymorphic fields (sealed interface + `@JsonTypeInfo`) in the same
object so each iteration exercises both branches, letting C2 record a
bimorphic profile during the warmup pass.

Warmup failures are caught and logged rather than aborting as a degraded
JIT profile is preferable to refusing to serve traffic.
@schlosna schlosna force-pushed the davids/jackson-warmup branch from e73b09b to d6c7ed4 Compare June 17, 2026 13:55
@schlosna schlosna marked this pull request as ready for review June 17, 2026 14:02
@changelog-app

changelog-app Bot commented Jun 17, 2026

Copy link
Copy Markdown

Successfully generated changelog entry!

Need to regenerate?

Simply interact with the changelog bot comment again to regenerate these entries.


📋Changelog Preview

💡 Improvements

  • Adds a JacksonWarmup type that serializes a representative set of payload shapes through fresh server and dialogue/client ObjectMappers (constructed via the same conjure-java factories used at runtime) so the JIT settles on the bimorphic BeanPropertyWriter.serializeAsField branch (typed vs untyped serializer) with full type-profile coverage before customer traffic perturbs it.

    This works around OpenJDK C2 JIT deoptimization storm where uncommon_trap isnonnull triggers continuous deoptimization as seen in https://bugs.openjdk.org/browse/JDK-8330258 &
    https://bugs.openjdk.org/browse/JDK-8374307 . The repeated deoptimization can be observed by jdk.Deoptimization JFR events at com.fasterxml.jackson.databind.ser.BeanPropertyWriter if (_typeSerializer == null) site, and then re-enters unstable_if deopt thrash on the JSON serialization path when a perturbing workload arrives.

    The warmup payloads interleave monomorphic fields (concrete types) with polymorphic fields (sealed interface + @JsonTypeInfo) in the same object so each iteration exercises both branches, letting C2 record a bimorphic profile during the warmup pass.

    Warmup failures are caught and logged rather than aborting as a degraded JIT profile is preferable to refusing to serve traffic. (#3512)

@schlosna schlosna requested review from a team and bjlaub June 17, 2026 21:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant