-
Notifications
You must be signed in to change notification settings - Fork 669
Description
[READ] Step 1: Are you in the right place?
Issues filed here should be about bugs in the code in this repository. If you have a general
question, need help debugging, or fall into some other category use one of these other channels:
- For general technical questions, post a question on StackOverflow
with the firebase tag. - For general Firebase discussion, use the
firebase-talk google group. - For help troubleshooting your application that does not fall under one of the above categories,
reach out to the personalized Firebase support channel.
[REQUIRED] Step 2: Describe your environment
- Android Studio version: Android Studio Otter | 2025.2.1 Patch 1
- Firebase Component: Firebase Crashlytics (firebase-crashlytics)
- Component version: BOM 34.3.0
[REQUIRED] Step 3: Describe the problem
We are seeing ANRs during app cold start caused by CrashlyticsRegistrar.<clinit> eagerly initializing a kotlinx.coroutines Mutex on the main thread. When Firebase discovers components via ComponentDiscovery, it reflectively loads CrashlyticsRegistrar using Class.forName(), which triggers its static initializer. This creates a MutexImpl, which cascades into initializing the entire kotlinx.coroutines synchronization infrastructure (SemaphoreAndMutexImpl, SemaphoreSegment, SemaphoreKt, ConcurrentLinkedListNode, SystemPropsKt) all via <clinit> blocks on the main thread.
This happens before Application.onCreate() is called, inside FirebaseInitProvider.onCreate(). On budget devices (Realme, Vivo), this class loading chain exceeds the ANR timeout.
We have 10 stack traces all showing the same pattern, caught at different points in the cascade. All other threads are idle no lock contention is involved.
Affected devices: Budget Realme and Vivo phones
Android versions: Android 15 (SDK 35), Android 16 Beta (SDK 36)
Visibility: Foreground
Firebase BOM: 34.3.0
kotlinx-coroutines: 1.10.2
androidx.collection: 1.5.0
Steps to reproduce:
Cannot reproduce locally. Observed in production via Playstore reports on budget devices during cold start.
Stack traces:
All traces share this common path:
FirebaseInitProvider.onCreate()
→ FirebaseApp.initializeApp()
→ ComponentRuntime.<init>()
→ ComponentDiscovery$$ExternalSyntheticLambda0.get() [Class.forName]
→ CrashlyticsRegistrar.<clinit>
→ MutexImpl.<init>
→ SemaphoreAndMutexImpl.<init>
Below the SemaphoreAndMutexImpl.<init>, the traces are caught at different points in the class initialization cascade:
Variant 1 - ConcurrentLinkedListNode.<clinit> → AtomicReferenceFieldUpdater:
"main" tid=1 Runnable
at java.lang.Class.getDeclaredField (Native method)
at java.util.concurrent.atomic.AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl.<init>
at java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater
at kotlinx.coroutines.internal.ConcurrentLinkedListNode.<clinit>
at kotlinx.coroutines.sync.SemaphoreAndMutexImpl.<init>
at kotlinx.coroutines.sync.MutexImpl.<init>
at com.google.firebase.crashlytics.CrashlyticsRegistrar.<clinit>
at java.lang.Class.classForName (Native method)
at java.lang.Class.forName
at com.google.firebase.components.ComponentDiscovery$$ExternalSyntheticLambda0.get
at com.google.firebase.components.ComponentRuntime.<init>
at com.google.firebase.FirebaseApp.<init>
at com.google.firebase.FirebaseApp.initializeApp
at com.google.firebase.provider.FirebaseInitProvider.onCreate
Variant 2 - Segment.<clinit>:
"main" tid=1 Runnable
at kotlinx.coroutines.internal.Segment.<clinit>
at kotlinx.coroutines.sync.SemaphoreAndMutexImpl.<init>
at kotlinx.coroutines.sync.MutexImpl.<init>
at com.google.firebase.crashlytics.CrashlyticsRegistrar.<clinit>
...same lower frames...
Variant 3 - SemaphoreKt.<clinit> → systemProp:
"main" tid=1 Runnable
at kotlinx.coroutines.internal.InlineList.systemProp
at kotlinx.coroutines.sync.SemaphoreKt.<clinit>
at kotlinx.coroutines.sync.SemaphoreSegment.<init>
at kotlinx.coroutines.sync.SemaphoreAndMutexImpl.<init>
at kotlinx.coroutines.sync.MutexImpl.<init>
at com.google.firebase.crashlytics.CrashlyticsRegistrar.<clinit>
...same lower frames...
Variant 4 -SystemPropsKt.<clinit> → Runtime.availableProcessors:
"main" tid=1 Runnable
at libcore.io.ForwardingOs.sysconf
at java.lang.Runtime.availableProcessors
at kotlinx.coroutines.internal.SystemPropsKt__SystemPropsKt.<clinit>
at kotlinx.coroutines.internal.InlineList.systemProp
at kotlinx.coroutines.sync.SemaphoreKt.<clinit>
at kotlinx.coroutines.sync.SemaphoreSegment.<init>
at kotlinx.coroutines.sync.SemaphoreAndMutexImpl.<init>
at kotlinx.coroutines.sync.MutexImpl.<init>
at com.google.firebase.crashlytics.CrashlyticsRegistrar.<clinit>
...same lower frames...
Variant 5 - SemaphoreKt.<clinit> → findSegmentInternal:
"main" tid=1 Runnable
at kotlinx.coroutines.internal.InlineList.findSegmentInternal
at kotlinx.coroutines.sync.SemaphoreKt.<clinit>
at kotlinx.coroutines.sync.SemaphoreSegment.<init>
at kotlinx.coroutines.sync.SemaphoreAndMutexImpl.<init>
at kotlinx.coroutines.sync.MutexImpl.<init>
at com.google.firebase.crashlytics.CrashlyticsRegistrar.<clinit>
...same lower frames...
What happened? How can we make the problem occur? This could be a description, log/console output,
etc.
Relevant Code:
// No app code involved. ANR occurs before Application.onCreate()
// during ContentProvider initialization triggered by the system.
// CrashlyticsRegistrar is loaded reflectively by ComponentDiscovery
// via Class.forName(), triggering its static initializer on the main thread.
