Analysis of the customer-provided Perfetto trace (sentryinvest) showed reflective class-availability probing is a meaningful part of SentryAndroid.init main-thread cost: io.sentry.util.LoadClass.isClassAvailable/loadClass → Class.forName ran ~22-24 times during init, and the calls triggered unrelated static initializers (e.g. androidx.compose.ui.node.Owner.<clinit>, FragmentLifecycleIntegration.<clinit>) plus ClassNotFoundException for absent integrations.
This is delivered as a stacked PR (collection #5634), squash-merged into main once the three stack PRs below merge.
Changes
[1] Probe without initializing — #5635
LoadClass.loadClass used Class.forName(name) (initializes the class). Switched to Class.forName(name, false, classLoader) so a mere availability probe no longer runs the class's static initializer; it initializes lazily on first real use.
[2] Cache lookups + collapse double-probes — #5636
Class presence is fixed for the process lifetime, so LoadClass now caches results (including "not available"). Also collapsed the isClassAvailable-then-loadClass double Class.forName in SpanFactoryFactory and ScopesStorageFactory into a single loadClass call.
[3] Gate Compose probes — #5637
The Compose gesture / view-hierarchy class probes ran during init unconditionally. Gated the gesture locators behind enableUserInteractionBreadcrumbs || enableUserInteractionTracing and the Compose view-hierarchy exporter behind attachViewHierarchy, so the Compose probes only run when a feature that consumes them is enabled.
Pixel 3 benchmarks (Android 12, ART method trace → Perfetto trace_processor)
- [1] Probing a class with a static initializer:
<clinit> invocations 1 → 0.
- [2] Repeat probe of an absent class (untraced): 25,872 ns → 137 ns per probe (~188×).
- [3] Features off: Compose class probes 3 → 0,
ClassNotFoundException constructions 9 → 0.
Follow-up (not in this issue)
Build-time elimination of isClassAvailable via the Sentry Android Gradle Plugin (generated availability constants), which would remove the remaining integration probes entirely.
Analysis of the customer-provided Perfetto trace (
sentryinvest) showed reflective class-availability probing is a meaningful part ofSentryAndroid.initmain-thread cost:io.sentry.util.LoadClass.isClassAvailable/loadClass→Class.forNameran ~22-24 times during init, and the calls triggered unrelated static initializers (e.g.androidx.compose.ui.node.Owner.<clinit>,FragmentLifecycleIntegration.<clinit>) plusClassNotFoundExceptionfor absent integrations.This is delivered as a stacked PR (collection #5634), squash-merged into
mainonce the three stack PRs below merge.Changes
[1] Probe without initializing — #5635
LoadClass.loadClassusedClass.forName(name)(initializes the class). Switched toClass.forName(name, false, classLoader)so a mere availability probe no longer runs the class's static initializer; it initializes lazily on first real use.[2] Cache lookups + collapse double-probes — #5636
Class presence is fixed for the process lifetime, so
LoadClassnow caches results (including "not available"). Also collapsed theisClassAvailable-then-loadClassdoubleClass.forNameinSpanFactoryFactoryandScopesStorageFactoryinto a singleloadClasscall.[3] Gate Compose probes — #5637
The Compose gesture / view-hierarchy class probes ran during init unconditionally. Gated the gesture locators behind
enableUserInteractionBreadcrumbs || enableUserInteractionTracingand the Compose view-hierarchy exporter behindattachViewHierarchy, so the Compose probes only run when a feature that consumes them is enabled.Pixel 3 benchmarks (Android 12, ART method trace → Perfetto trace_processor)
<clinit>invocations 1 → 0.ClassNotFoundExceptionconstructions 9 → 0.Follow-up (not in this issue)
Build-time elimination of
isClassAvailablevia the Sentry Android Gradle Plugin (generated availability constants), which would remove the remaining integration probes entirely.