Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/AmolPardeshi99/android-performance-skills/llms.txt

Use this file to discover all available pages before exploring further.

Android’s ART runtime uses a tracing garbage collector that walks every reachable reference starting from a fixed set of GC roots. Any object reachable from a root survives collection — a leak is simply an object that remains reachable even though the app no longer needs it. Understanding this model is the foundation for diagnosing every type of memory leak.
“Reachable” and “no longer needed by the app” are two different things. The GC only knows about reachability — it cannot know whether your app still intends to use an object. An Activity that has been destroyed is no longer needed, but if a static singleton holds a reference to it, the GC still considers it reachable and will not collect it. This is the essence of every Android memory leak.

0.1 How ART garbage collection works

ART uses a tracing GC: starting from a fixed set of GC roots, it walks every reachable reference chain. Any object reachable from a root survives; anything not reachable is collected. A leak is simply an object that is still reachable from a root even though the app no longer needs it. GC roots on Android:
Root typeExamples
Static fieldscompanion object fields, object singletons, Java static variables
Thread stacksLocal variables and parameters on any running thread’s call stack
JNI globalsReferences held by native code via NewGlobalRef
Running threadsThe Thread object itself, plus everything it references
System class loaderClass objects and their static fields
The classic Android leak always follows the same structural pattern — a GC root anchors a long-lived object that holds a reference chain down to an Activity or View:
// The classic Android leak chain
// Static field (GC root)
//   └─ Singleton / long-lived object
//        └─ Listener / callback / lambda
//             └─ Activity / Fragment (captures `this` implicitly)
//                  └─ ViewBinding / View tree  (tens of MBs)

0.2 Java/Kotlin reference strength

Not all references are equal. Java and Kotlin provide four reference strengths that control when the GC is allowed to collect the referenced object.
Reference typeGC behaviourAndroid use case
Strong (default)Object survives as long as the reference existsEverything unless you explicitly weaken it
SoftReferenceCollected only when JVM is low on memoryImage caches — JVM decides when to evict
WeakReferenceCollected at next GC cycle when no strong refs remainSafe back-reference from long-lived object to Activity
PhantomReferenceCollected after finalization; never returns objectLow-level cleanup hooks; rarely used in app code
Use WeakReference when a long-lived object needs to call back into a shorter-lived one (such as an Activity). If the Activity has been destroyed and collected, get() returns null and the call is safely skipped:
// WeakReference pattern — safe back-reference without preventing GC
class LocationManager {
    private var weakCallback: WeakReference<LocationCallback>? = null

    fun register(cb: LocationCallback) { weakCallback = WeakReference(cb) }

    fun onLocationUpdate(loc: Location) {
        weakCallback?.get()?.onLocation(loc)  // null-safe; GC'd callback is simply skipped
            ?: cleanup()                       // callback was GC'd → clean up subscription
    }
}

0.3 Why Activity/Fragment leaks are especially costly

A leaked Activity retains its entire View tree — often 5–50 MB per instance. This is not a single object leak; it is the retention of an entire object graph including every View, every loaded Bitmap, the Context, the WindowManager reference, theme resources, and any ViewBinding object that points to all of the above.
A single leaked Activity retains:
  • Its entire View tree (every TextView, RecyclerView, ImageView, etc.)
  • Every Bitmap loaded into those views
  • The Context — which itself references the WindowManager, LayoutInflater, and theme resources
  • Any ViewBinding object referencing all of the above
On a typical screen this is 5–50 MB per leaked instance. With Navigation Component back-stack depth of 5, one systematic leak pattern produces 25–250 MB of unreclaimable heap — guaranteed OOM or LMK kill on mid-range devices.

Build docs developers (and LLMs) love