Skip to content

perf(core): [Init Reflection 2] Cache class lookups and collapse double probes#5636

Open
runningcode wants to merge 3 commits into
no/perf-init-reflection-no-initfrom
no/perf-init-reflection-cache
Open

perf(core): [Init Reflection 2] Cache class lookups and collapse double probes#5636
runningcode wants to merge 3 commits into
no/perf-init-reflection-no-initfrom
no/perf-init-reflection-cache

Conversation

@runningcode

@runningcode runningcode commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

PR Stack (Init Reflection)


Part of JAVA-587

📜 Description

Two related reflection-cost reductions on top of [Init Reflection 1]:

  • Cache LoadClass results. Whether a class is on the classpath doesn't change during the process lifetime, so results (including "not available") are cached in a static map. Repeated probes of the same class skip Class.forName entirely — and skip the ClassNotFoundException thrown for absent classes.
  • Collapse double-probes. SpanFactoryFactory and ScopesStorageFactory called isClassAvailable(X) and then loadClass(X) — two Class.forName calls for the same class. They now call loadClass(X) once and null-check.

💡 Motivation and Context

Second of three stacked PRs reducing reflection cost on the init path (customer-provided Perfetto trace, Reduce SDK init time [Android]).

💚 How did you test it?

Existing LoadClassTest, factory, and Sentry init tests pass; the cache returns identical availability results.

📝 Checklist

  • I added tests to verify the changes.
  • No new PII added or SDK only sends newly added PII if sendDefaultPII is enabled.
  • No breaking change or entry added to the changelog.

🔮 Next steps

PR 3 gates the Compose class probes behind the features that consume them.

⏱️ Pixel 3 benchmark (untraced timing)

Repeatedly probing the same absent class (2000 calls × 6 rounds; cache hits after the first miss):

old (uncached forName each call) new (cached)
per probe 25,872 ns 137 ns

~188× faster for repeat probes — once the first miss caches NOT_AVAILABLE, later probes skip both the Class.forName call and the ClassNotFoundException. (Traced counts are noisy here due to background class loading, so this is measured untraced.)

⚠️ Merge this PR using a merge commit (not squash). Only the collection branch is squash-merged into main.

runningcode and others added 2 commits June 25, 2026 13:33
Class availability is fixed for the lifetime of the process, so cache
LoadClass results to avoid repeated Class.forName lookups (and the
exceptions thrown for absent classes) when the same class is probed more
than once. Also collapse the isClassAvailable-then-loadClass double
probe in the OpenTelemetry span/scopes factories into a single
loadClass call.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sentry

sentry Bot commented Jun 25, 2026

Copy link
Copy Markdown

📲 Install Builds

Android

🔗 App Name App ID Version Configuration
SDK Size io.sentry.tests.size 8.45.0 (1) release

⚙️ sentry-android Build Distribution Settings

@linear-code

linear-code Bot commented Jun 25, 2026

Copy link
Copy Markdown

JAVA-587

@runningcode runningcode marked this pull request as ready for review June 25, 2026 12:03
@github-actions

Copy link
Copy Markdown
Contributor

Performance metrics 🚀

  Plain With Sentry Diff
Startup time 339.47 ms 403.24 ms 63.77 ms
Size 0 B 0 B 0 B

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