skip to content

Does the frozen-condition behavior only apply to GraalVM native images, or also to JVM apps? How would you reproduce and diagnose it?

level: middleimportance: should knowfreq 35%

answer

  1. AOT != native; freeze is an AOT property
  2. spring.aot.enabled=true reproduces on the JVM
  3. actuator /beans + containsBean to check presence
  4. getActiveProfiles vs missing @Profile beans
  5. ConditionEvaluationReport in JIT to predict freeze

basics

~10 s

It also applies on the JVM. Setting spring.aot.enabled=true runs the same generated, frozen-bean initializer. So you can reproduce and debug frozen-condition issues on a normal JVM before ever building a native image.

solid answer

~40 s

The freeze comes from **AOT processing**, not from GraalVM. The Spring Boot `processAot` task generates an `ApplicationContextInitializer` with a fixed bean set; a GraalVM native image *always* uses it, but a plain JVM app also uses it when you set `spring.aot.enabled=true` (Boot's `bootRun`/launch supports an AOT mode). This is deliberate: you can run the AOT-optimized app on the JVM to validate that conditions, profiles, and registrars resolve correctly at build time, catching frozen-condition bugs cheaply before the slow native compile. To diagnose, enable AOT on the JVM, check whether a bean is present via actuator `beans` or `ApplicationContext.containsBean`, and compare active profiles (`Environment.getActiveProfiles()`) against which profile-gated beans actually exist.

go deeper

for a junior

Know AOT and native image are not the same thing.

for a middle

Explain that spring.aot.enabled=true reproduces the frozen behavior on the JVM and how to check bean presence.

for a senior

Use it as a fast CI gate; correlate ConditionEvaluationReport, active profiles, and actuator beans to diagnose.

for a principal

Standardize an AOT-on-JVM verification stage before native builds and control the build environment so the frozen snapshot is reproducible.

## AOT vs native image People conflate 'AOT' with 'GraalVM native image', but they are distinct: - **AOT processing** is a Spring build step (`org.springframework.aot`) that evaluates conditions and generates Java source + `RuntimeHints`. Triggered by the Boot `processAot` task/`process-aot` goal. - **GraalVM native image** is a separate ahead-of-time *compilation to a native binary* that **requires** AOT processing and adds the closed-world reflection model. So the **frozen bean set** is a property of AOT, present in **both** modes. ## Reproducing on the JVM Spring Boot supports running the AOT-optimized application on the ordinary JVM. Setting `spring.aot.enabled=true` (Boot does this for you when launching the AOT-generated app; the property is the switch) makes the context use the **generated initializer** instead of live condition evaluation. This is the recommended way to test AOT behavior without paying for a native build (which can take minutes). ## Diagnosing frozen-condition problems 1. **Enable AOT on the JVM** and start the app. 2. **Check bean presence**: Spring Boot Actuator's `/actuator/beans`, or `ctx.containsBean(name)`, or `ctx.getBeanNamesForType(...)`. A bean present in JIT mode but missing in AOT mode confirms a build-time condition dropped it. 3. **Compare profiles**: log `Environment.getActiveProfiles()` and cross-check against `@Profile` beans — the classic 'profile active but bean missing' symptom. 4. **Condition report**: in JIT mode the `ConditionEvaluationReport` (debug=true) explains why a bean matched/didn't; use it to understand what AOT would have frozen. 5. **Build-time logs/warnings**: watch `processAot` output for profile warnings and generated-source locations (`build/generated/aotSources`). ## Gotchas - The build environment (env vars, present property files, classpath) at `processAot` time silently shapes the frozen result — CI vs local differences can cause 'works on my machine' native bugs. - Turning on AOT can also surface **proxy/reflection** gaps that JIT tolerated. - Don't AOT-build with a `test`/dev profile accidentally active. ## When to use Always run the AOT profile on the JVM in CI as a gate **before** the native compile — it's fast and catches the majority of frozen-condition and hint issues.

  • Why run the AOT profile on the JVM in CI instead of just building the native image?
    The native compile is slow (minutes) and resource-heavy. The JVM AOT run uses the same frozen initializer and hints, so it catches most condition/profile/reflection problems in seconds, gating the expensive native build.

saying these in an interview costs you the question

  • Insisting frozen conditions only exist in GraalVM native images
  • Not knowing spring.aot.enabled lets you reproduce it on the JVM
  • Ignoring that the build environment shapes the frozen result
  • Never checking actuator /beans to confirm bean presence differences

context