Skip to content

Add support for AppCDS and AOT#5644

Open
Alexander Maryanovsky (m-sasha) wants to merge 25 commits into
masterfrom
m-sasha/aot
Open

Add support for AppCDS and AOT#5644
Alexander Maryanovsky (m-sasha) wants to merge 25 commits into
masterfrom
m-sasha/aot

Conversation

@m-sasha

@m-sasha Alexander Maryanovsky (m-sasha) commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

AppCDS and AOT are new mechanisms in the JDK/JVM which allow creating a "class archive" that helps the JVM to load classes much faster than normally, significantly reducing startup time.

Quick-start

To create/use this archive, the application needs to be run with special JVM flags.

Note that the distributable will be larger than usual because at minimum, the archive of the JRE classes needs to be packaged with the app.

The API is a new section in compose.desktop { application { ... } }, e.g.

compose.desktop {
    application {
        aot {
            mode = AotMode.AotPrebuild
            logging = true
        }
    }
}

There are currently four modes:

None

The "normal" mode where nothing special happens and everything runs as before. This is the default.

AppCdsAuto

In this mode, an AppCDS class archive is created when the end-user first runs the app.
Requires JDK 19 or later.

Advantages:

  • Simplest - no additional step is needed to build the archive.
  • Creates a smaller distributable.
  • Subsequent executions of the app will be faster.

Drawbacks:

  • The archive is not available at the first execution of the app, so it is slower (possibly even slower than regular execution).
  • The archive is created at shutdown time of the first execution, which also takes a little longer.

AppCdsPrebuild

In this mode Gradle executes a "training run" of the app before packaging it to create the AppCDS class archive.
Requires JDK 21 or later.

Advantages:

  • Fast startup, including the first run.

Drawbacks:

  • Requires an additional step of running the app when building the distributable. See the "Training Run" section for details.
  • The distributable is larger because it includes the archive of the app's classes.

AotPrebuild

This mode is similar to AppCdsPrebuild, but it uses the JDK 25 AOT feature which is an enhancement of AppCDS that adds even more information to the class archive. It improves linking and JIT warmup too.

This mode also requires a "training run" of the app before packaging it to create the AOT class archive.
Requires JDK 25 or later.

Advantages:

  • Fast startup, including the first run.
  • Potentially faster startup than AppCdsPrebuild.
  • Faster JIT warmup.

Drawbacks:

  • Requires an additional step of running the app when building the distributable. See the "Training Run" section for details.
  • The distributable is larger because it includes the archive of the app's classes.

Training Run

In the "Prebuild" modes, the Compose Multiplatform Gradle plugin runs the app (the "Training Run") when packaging it for distribution to create the class archive. When it does this, it sets a system property compose.aot.training-run to true, letting the app know that it's being run to create the archive. The app, when given this flag, should cause all the classes it wants to be in the archive to be loaded.

This can be very simple if the developer only wants to optimize startup. In this case, and if building the distributable manually, the developer can just manually quit the app after it is run. If building automatically (e.g. on a CI/CD), the app should check for the flag, wait until it has finished starting up, and exit. Something like this will work for most apps:

application {
    ...
    if (System.getProperty("compose.aot.training-run") == "true") {
        LaunchedEffect(Unit) {
            delay(10.seconds)  // Or wait for a custom event indicating the app finished startup
            exitApplication()
        }
    }
}

We could also add some support in Compose when running with this flag.

  • An isComposeIdle state, so the app can wait on that.
  • A completely automatic mode when we exit the app when isComposeIdle becomes true after startup.

Of course the developer may want to include classes other than the ones used on startup. For example, there may be a feature/screen in the app that loads many classes, and therefore takes a long time when used the first time. The app should then "use" this feature (or show the screen) and then quit.

Note that when building the distributable on a CI/CD machine, it will need to be able to run the app, and therefore will need to have a graphical environment. In the future, maybe we could execute the app using ImageComposeScene and not require a graphical environment.

Startup Time comparison on a small project

All values in milliseconds

macOS (M1 Ultra)

Cold start

Build mode Shell → Main Main → Window Total Speedup (vs. no AOT)
No AOT 1133 884 2017
AppCDS Prebuild 48 478 526 3.8×
AOT Prebuild 67 357 424 4.8×

Hot start (2nd run)

Build mode Shell → Main Main → Window Total Speedup (vs. no AOT)
No AOT 76 860 936
AppCDS Prebuild 46 488 534 1.8×
AOT Prebuild 69 368 437 2.1×

Windows (Dell Precision 5690)

Cold start

Build mode Shell → Main Main → Window Total Speedup (vs. no AOT)
No AOT 145 1211 1356
AppCDS Prebuild 110 718 828 1.6×
AppCDS Prebuild (relocated*) 106 1111 1217 1.1×
AOT Prebuild 130 520 650 2.1×
AOT Prebuild (relocated*) 402 517 919 1.5×

Hot start (2nd run)

Build mode Shell → Main Main → Window Total Speedup (vs. no AOT)
No AOT 137 1193 1330
AppCDS Prebuild 113 688 801 1.7×
AppCDS Prebuild (relocated*) 103 1092 1195 1.1×
AOT Prebuild 126 530 656 2.0×
AOT Prebuild (relocated*) 391 526 917 1.5×

*Executed in a such a way that the jars at the classpath with which the app was packages don't exist.
This is important due to https://youtrack.jetbrains.com/issue/JBR-9098/AppCDS-writes-local-paths-into-.jsa-archive

Fixes https://youtrack.jetbrains.com/issue/CMP-8338/Support-AppCDS-in-Compose-for-Desktop
Fixes https://youtrack.jetbrains.com/issue/CMP-10425/AOT-Compilation-for-desktop-distributables

Testing

Tested manually on a demo app.
The app is at https://github.com/m-sasha/DesktopPlaygroundAot

This should be tested by QA

Release Notes

Highlights - Desktop

  • Implemented support for AppCDS and AOT, which can significantly speed up application startup.

@m-sasha

Alexander Maryanovsky (m-sasha) commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

I haven't (re-)tested this on a demo app yet, but the tests seem to pass locally.

@m-sasha

Copy link
Copy Markdown
Contributor Author

There is an issue remaining from the previous attempt to support AppCDS where the JVM "looks at" the class files on the classpath even though they are present in the archive. On Windows this results in failing file lookups which take a long time and this negates a large part of the benefit of AppCDS (but it's still faster than without AppCDS).

I'm still in conversation with the IJ/JBR team to hopefully fix this one day, but for now I guess we'll have to live with it.

…hen running the app to create the archive in Prebuild mode.
@terrakok

Copy link
Copy Markdown
Member

to make the CI happy, please, update the compose version in gradle.properties to 1.12.0-beta02

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.

2 participants