Diagnosing and Resolving Memory Leaks in Android Activities and Fragments
Learn how to diagnose and fix memory leaks in Android Activities and Fragments using LeakCanary, Android Studio Profiler, and WeakReference patterns.
16 Sept 2026, 02:10 UTC

A memory leak occurs in Android when a long-lived object holds a reference to a short-lived object—typically an Activity or Fragment—preventing the Garbage Collector (GC) from reclaiming its memory. This leads to increased heap pressure, UI stuttering (jank) caused by frequent GC events, and eventual OutOfMemoryError (OOM) crashes.
Recognizing the Condition
Memory leaks are often invisible until the app reaches a critical memory threshold. Use the following table to map observed behavior to likely causes:
| Symptom | Likely Cause |
|---|---|
| Rising Heap Usage | Memory does not return to baseline after navigating away from a screen. |
| UI Stuttering/Jank | Frequent GC cycles attempting to free memory to avoid an OOM crash. |
| OOM Crashes | The heap is exhausted, typically during navigation or high-intensity tasks. |
Diagnostic Workflow
Follow these checks in order to isolate the leak and identify the reference chain keeping the object alive.
1. Automated Detection with LeakCanary
LeakCanary is the industry standard for detecting leaks by monitoring the heap and reporting reference chains when an Activity is destroyed.
Add the dependency to your build.gradle file. Use debugImplementation to ensure the library is not included in production builds:
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
Verification: Run the app, navigate to the suspected screen, and then navigate back. If a leak is detected, LeakCanary will trigger a notification showing the "Reference Chain"—the path of objects preventing the Activity from being collected.
2. Manual Heap Analysis via Android Studio Profiler
If automated tools are unavailable, use the Memory Profiler to verify the leak manually:
- Open Tools > Profiler and select the running app process.
- Click the Memory timeline.
- Perform a "stress test": Open and close the suspected Activity several times.
- Click Capture Heap Dump and then Record.
- Filter the class list by the Activity's name (e.g.,
MyDetailActivity). - Check: If the instance count is greater than 0 after the Activity has been closed, a leak is present.
Common Culprits and Engineering Fixes
Non-Static Inner Classes and Handlers
Non-static inner classes (and anonymous classes) hold an implicit reference to the outer class. If a background Handler or Thread uses an inner class, the Activity cannot be destroyed until the background task completes.
Leaky Pattern:
class MyActivity : Activity() {
private val handler = object : Handler() {
override fun handleMessage(msg: Message) {
// Implicitly holds a reference to MyActivity
updateUI()
}
}
}
The Fix: Convert the inner class to a static class (or a separate class in Kotlin) and use a WeakReference. A WeakReference allows the GC to reclaim the object even if the reference still exists.
class MyHandler(activity: MyActivity) : Handler() {
private val activityRef = WeakReference(activity)
override fun handleMessage(msg: Message) {
val activity = activityRef.get() ?: return // Return if activity was collected
activity.updateUI()
}
}
Unregistered Listeners and Observers
Registering a BroadcastReceiver, location listener, or custom callback without a corresponding unregistration keeps the Activity alive in the system's registry.
The Fix: Pair every registration with a de-registration in the appropriate lifecycle method (usually onStop() or onDestroy()).
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(myReceiver)
viewModel.data.removeObserver(myObserver)
}
Limitations and Risks
- NullPointerExceptions: When using
WeakReference, always check if.get()returns null before accessing the object, as the GC may have already reclaimed it. - Static Caching: Avoid using static variables for caching Activity-related data. If caching is required, use an
LruCacheto limit memory overhead.
Rollback and Verification
If a fix introduces instability (e.g., crashes due to null references), revert the WeakReference implementation to the previous state and implement a manual cleanup method in onDestroy() to nullify the reference.
Final Verification: Repeat the Android Studio Profiler heap dump. The instance count for the Activity should be 0 after closing the screen.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.