From 62133fc24f5e3d9c876d01b339bb7b393be97e86 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Thu, 25 Jun 2026 13:31:56 +0200 Subject: [PATCH 1/3] perf(core): Probe class availability without initializing the class LoadClass.loadClass used Class.forName(name) which initializes the class. Used purely for availability probing during init, this eagerly runs unrelated static initializers (e.g. Compose's Owner, the fragment integration). Use Class.forName(name, false, classLoader) so the class is only initialized lazily on first real use. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../main/java/io/sentry/util/LoadClass.java | 4 +- .../test/java/io/sentry/util/LoadClassTest.kt | 49 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 sentry/src/test/java/io/sentry/util/LoadClassTest.kt diff --git a/sentry/src/main/java/io/sentry/util/LoadClass.java b/sentry/src/main/java/io/sentry/util/LoadClass.java index 1946ce8381f..c639f62b9c4 100644 --- a/sentry/src/main/java/io/sentry/util/LoadClass.java +++ b/sentry/src/main/java/io/sentry/util/LoadClass.java @@ -20,7 +20,9 @@ public class LoadClass { */ public @Nullable Class loadClass(final @NotNull String clazz, final @Nullable ILogger logger) { try { - return Class.forName(clazz); + // Don't initialize the class just to probe for availability; it gets initialized lazily on + // first use. This avoids running unrelated static initializers during SDK init. + return Class.forName(clazz, false, LoadClass.class.getClassLoader()); } catch (ClassNotFoundException e) { if (logger != null) { logger.log(SentryLevel.INFO, "Class not available: " + clazz); diff --git a/sentry/src/test/java/io/sentry/util/LoadClassTest.kt b/sentry/src/test/java/io/sentry/util/LoadClassTest.kt new file mode 100644 index 00000000000..0c8dacd1c54 --- /dev/null +++ b/sentry/src/test/java/io/sentry/util/LoadClassTest.kt @@ -0,0 +1,49 @@ +package io.sentry.util + +import kotlin.test.Test +import kotlin.test.assertFalse +import kotlin.test.assertNotNull +import kotlin.test.assertNull + +class LoadClassTest { + @Test + fun `loadClass returns the class when it is available`() { + assertNotNull(LoadClass().loadClass("io.sentry.SentryEvent", null)) + } + + @Test + fun `loadClass returns null when the class is not available`() { + assertNull(LoadClass().loadClass("io.sentry.ThisClassDoesNotExist", null)) + } + + @Test + fun `isClassAvailable reflects whether the class is on the classpath`() { + val loadClass = LoadClass() + assertNotNull(loadClass.loadClass("io.sentry.SentryEvent", null)) + assertFalse( + loadClass.isClassAvailable("io.sentry.ThisClassDoesNotExist", null as io.sentry.ILogger?) + ) + } + + @Test + fun `loadClass does not run the static initializer of the probed class`() { + // Reading the flag initializes the flag holder, not the probe. + assertFalse(LoadClassNoInitFlag.initialized) + + // Obtaining the name via ::class.java does not initialize the probe either. + LoadClass().loadClass(LoadClassNoInitProbe::class.java.name, null) + + // Availability probing must not trigger the probe's static initializer. + assertFalse(LoadClassNoInitFlag.initialized) + } +} + +private object LoadClassNoInitFlag { + @JvmField var initialized = false +} + +private object LoadClassNoInitProbe { + init { + LoadClassNoInitFlag.initialized = true + } +} From 7a33c9c04ea2f84c683ee7523e5275f61d76dd54 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Thu, 25 Jun 2026 13:32:38 +0200 Subject: [PATCH 2/3] changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48a1115f8ae..92cd4b4eec6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Internal + +- Probe class availability without initializing the class during SDK init ([#5635](https://github.com/getsentry/sentry-java/pull/5635)) + ## 8.45.0 ### Features From 552d466d7843d7d23ec4d6f10aedf6cf9cb83f72 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Thu, 25 Jun 2026 13:45:57 +0200 Subject: [PATCH 3/3] changelog: move init reflection entries to Performance --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92cd4b4eec6..ca6a5b91ec7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## Unreleased -### Internal +### Performance - Probe class availability without initializing the class during SDK init ([#5635](https://github.com/getsentry/sentry-java/pull/5635))