Skip to content

Fix slow class loading of relocated app with AppCDS/AOT - #618

Open
Alexander Maryanovsky (m-sasha) wants to merge 1 commit into
JetBrains:jbr25from
m-sasha:m-sasha/fix-slow-aot-classloading
Open

Fix slow class loading of relocated app with AppCDS/AOT#618
Alexander Maryanovsky (m-sasha) wants to merge 1 commit into
JetBrains:jbr25from
m-sasha:m-sasha/fix-slow-aot-classloading

Conversation

@m-sasha

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

Copy link
Copy Markdown

This PR addresses the main problem reported in JBR-9098 AppCDS writes local paths into .jsa archive, namely the slowdown in application startup.

The PR is the result of an investigation by myself, following a session with Claude Code Opus 4.8, which verified the problem and produced a fix. I verified that the fix works (the slowdown is gone), but other than that I can't vouch for the code.

Below is a write-up by Claude:


Symptom

With an AppCDS (.jsa) or AOT cache created from an absolute classpath, moving the application directory and launching from the new location loses all of the archive's startup benefit — startup becomes slower than running with no archive at all (on Windows, ~866 ms vs ~390 ms baseline for a 2,000-class app; the never-moved case is ~183 ms). All classes still load from the archive; the time is spread evenly across every class load. A secondary, cross-platform symptom: archived classes in a relocated app get a null CodeSource.

Root cause

CDSProtectionDomain::get_shared_jar_url() uses null as its "not yet computed" sentinel and builds the URL from the raw dump-time classpath path. When the app has moved, that path no longer exists, so ClassLoaders.toFileURL() returns null after a full Path.toRealPath() canonicalization walk of the dead path — and because null is never cached, the walk is repeated for every class loaded from that entry. On Windows each walk is FindFirstFile per path component through the filter-driver stack (~30 metadata syscalls/class here), which is what makes it dominate startup; on macOS/Linux the same repeated walk is far cheaper and effectively invisible.

Separately, AOTClassLocationConfig::validate() already detects the relocation and accepts the archive via longest-common-prefix substitution — but then discards the substituted (live) path, so class loading still uses the stale one.

Solution

  1. Reuse the LCP substitution at runtime. Persist the accepted runtime LCP in validate() and add AOTClassLocationConfig::runtime_path(index), which returns the dump-time path with the substitution applied — i.e. where the entry actually lives now. Consume it in get_shared_jar_url() and the JVMTI classfile-stream path. This resolves successfully for a moved app, so the URL is computed once and cached, and CodeSource is correct again.
  2. Cache negative results. Add a per-entry _shared_jar_url_computed[] flag so the toFileURL upcall runs at most once per classpath entry even when it returns null (covers cases LCP can't fix, e.g. a relative dump-time path resolved against a different runtime CWD). The now-expected null return is documented; the debug-only assert(url_h.not_null()) is removed.

Relocated-app startup returns to the never-moved figure (866→183 ms jsa, 608→100 ms aot), metadata syscalls drop from ~61k to ~1.1k, and no non-moved scenario regresses. The bug reproduces on stock OpenJDK 25, so this is not JBR-specific.


@bourgesl

Laurent Bourgès (bourgesl) commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Should such low-level fix be proposed to OpenJDK for review instead ?
Or is it specific to JBR / DCEVM ?

@m-sasha

Copy link
Copy Markdown
Author

It's not specific to JBR, and we should definitely upstream it. But I think we should have someone from the JBR team review it first. Given that it was implemented by AI, we should make sure it's good first.

@bourgesl

Laurent Bourgès (bourgesl) commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

"I confirm that I make this contribution in accordance with the OpenJDK Interim AI Policy."
It says:
"Contributions in the OpenJDK Community must not include content generated, in part or in full, by large language models, diffusion models, or similar deep-learning systems. Content, in this context, includes but is not limited to source code, text, and images in OpenJDK Git repositories, GitHub pull requests, e-mail messages, wiki pages, and JBS issues."

No fix can be made by AI... should it be rewritten or ...?

@m-sasha

Copy link
Copy Markdown
Author

Hehe, I didn't read the text of that snippet; just assumed it was a standard rights-from-contributor thing.
Unchecked it for now then.

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