Skip to content
🤔 Documentation issue? Report it

//leakcanary-repo/shark/AndroidReferenceReaders/ACTIVITY_THREAD__NEW_ACTIVITIES

ACTIVITY_THREAD__NEW_ACTIVITIES

[jvm]\ ACTIVITY_THREAD__NEW_ACTIVITIES

ActivityThread.mNewActivity is a linked list of ActivityClientRecord that keeps track of activities after they were resumed, until the main thread is idle. This is used to report analytics to system_server about how long it took for the main thread to settle after resuming an activity. Unfortunately, if the main thread never becomes idle, all these new activities leak in memory.

We’d normally catch these with a pattern in AndroidReferenceMatchers, and we do have AndroidReferenceMatchers.ACTIVITY_THREAD__M_NEW_ACTIVITIES to do that, however this matching only works if none of the activities alive are waiting for idle. If any activity alive is still waiting for idle (which all the alive activities would be if they main thread is never idle) then ActivityThread.mActivities will reference an ActivityClientRecord through an ArrayMap and because ActivityClientRecord are reused that instance will also have its nextIdle fields set, so we’re effectively traversing the ActivityThread.mNewActivity from a completely different and unexpected entry point.

To fix that problem of broken pattern matching, we emit the mNewActivities field when finding an ActivityThread instance, and because custom ref readers have priority over the default instance field reader, we’re guaranteed that mNewActivities is enqueued before mActivities. Unfortunately, that also means we can’t rely on AndroidReferenceMatchers as those aren’t used here, so we recreate our own LibraryLeakReferenceMatcher.

We want to traverse mNewActivities before mActivities so we can’t set isLowPriority to true like we would for normal path tagged as source of leak. So we will prioritize going through all activities in mNewActivities, some of which aren’t destroyed yet (and therefore not leaking). Going through those paths of non leaking activities, we might find other leaks though. This would result in us tagging unrelated leaks as part of the mNewActivities leak. To prevent this, we traverse ActivityThread.mNewActivities as a linked list through ActivityClientRecord.nextIdle as a linked list, but we emit only ActivityClientRecord.activity fields if such activities are destroyed, which means any live activity in ActivityThread.mNewActivities will be discovered through the normal field navigation process and should go through ActivityThread.mActivities.

Functions

Name Summary
create [jvm]
open override fun create(graph: HeapGraph): ChainingInstanceReferenceReader.VirtualInstanceReferenceReader?

Properties

Name Summary
name [jvm]
val name: String
ordinal [jvm]
val ordinal: Int