1*d9e8da70SAndroid Build Coastguard WorkerOnce LeakCanary is installed, it automatically detects and report memory leaks, in 4 steps: 2*d9e8da70SAndroid Build Coastguard Worker 3*d9e8da70SAndroid Build Coastguard Worker1. Detecting retained objects. 4*d9e8da70SAndroid Build Coastguard Worker2. Dumping the heap. 5*d9e8da70SAndroid Build Coastguard Worker3. Analyzing the heap. 6*d9e8da70SAndroid Build Coastguard Worker4. Categorizing leaks. 7*d9e8da70SAndroid Build Coastguard Worker 8*d9e8da70SAndroid Build Coastguard Worker## 1. Detecting retained objects 9*d9e8da70SAndroid Build Coastguard Worker 10*d9e8da70SAndroid Build Coastguard WorkerLeakCanary hooks into the Android lifecycle to automatically detect when activities and fragments are destroyed and should be garbage collected. These destroyed objects are passed to an `ObjectWatcher`, which holds [weak references](https://en.wikipedia.org/wiki/Weak_reference) to them. LeakCanary automatically detects leaks for the following objects: 11*d9e8da70SAndroid Build Coastguard Worker 12*d9e8da70SAndroid Build Coastguard Worker* destroyed `Activity` instances 13*d9e8da70SAndroid Build Coastguard Worker* destroyed `Fragment` instances 14*d9e8da70SAndroid Build Coastguard Worker* destroyed fragment `View` instances 15*d9e8da70SAndroid Build Coastguard Worker* cleared `ViewModel` instances 16*d9e8da70SAndroid Build Coastguard Worker 17*d9e8da70SAndroid Build Coastguard WorkerYou can watch any objects that is no longer needed, for example a detached view or a destroyed presenter: 18*d9e8da70SAndroid Build Coastguard Worker 19*d9e8da70SAndroid Build Coastguard Worker```kotlin 20*d9e8da70SAndroid Build Coastguard WorkerAppWatcher.objectWatcher.watch(myDetachedView, "View was detached") 21*d9e8da70SAndroid Build Coastguard Worker``` 22*d9e8da70SAndroid Build Coastguard Worker 23*d9e8da70SAndroid Build Coastguard WorkerIf the weak reference held by `ObjectWatcher` isn't cleared after **waiting 5 seconds** and running garbage collection, the watched object is considered **retained**, and potentially leaking. LeakCanary logs this to Logcat: 24*d9e8da70SAndroid Build Coastguard Worker 25*d9e8da70SAndroid Build Coastguard Worker``` 26*d9e8da70SAndroid Build Coastguard WorkerD LeakCanary: Watching instance of com.example.leakcanary.MainActivity 27*d9e8da70SAndroid Build Coastguard Worker (Activity received Activity#onDestroy() callback) 28*d9e8da70SAndroid Build Coastguard Worker 29*d9e8da70SAndroid Build Coastguard Worker... 5 seconds later ... 30*d9e8da70SAndroid Build Coastguard Worker 31*d9e8da70SAndroid Build Coastguard WorkerD LeakCanary: Scheduling check for retained objects because found new object 32*d9e8da70SAndroid Build Coastguard Worker retained 33*d9e8da70SAndroid Build Coastguard Worker``` 34*d9e8da70SAndroid Build Coastguard Worker 35*d9e8da70SAndroid Build Coastguard WorkerLeakCanary waits for the count of retained objects to reach a threshold before dumping the heap, and displays a notification with the latest count. 36*d9e8da70SAndroid Build Coastguard Worker 37*d9e8da70SAndroid Build Coastguard Worker 38*d9e8da70SAndroid Build Coastguard Worker**Figure 1.** LeakCanary found 4 retained objects. 39*d9e8da70SAndroid Build Coastguard Worker 40*d9e8da70SAndroid Build Coastguard Worker``` 41*d9e8da70SAndroid Build Coastguard WorkerD LeakCanary: Rescheduling check for retained objects in 2000ms because found 42*d9e8da70SAndroid Build Coastguard Worker only 4 retained objects (< 5 while app visible) 43*d9e8da70SAndroid Build Coastguard Worker``` 44*d9e8da70SAndroid Build Coastguard Worker 45*d9e8da70SAndroid Build Coastguard Worker!!! info 46*d9e8da70SAndroid Build Coastguard Worker The default threshold is **5 retained objects** when the app is **visible**, and **1 retained object** when the app is **not visible**. If you see the retained objects notification and then put the app in background (for example by pressing the Home button), then the threshold changes from 5 to 1 and LeakCanary dumps the heap within 5 seconds. Tapping the notification forces LeakCanary to dump the heap immediately. 47*d9e8da70SAndroid Build Coastguard Worker 48*d9e8da70SAndroid Build Coastguard Worker## 2. Dumping the heap 49*d9e8da70SAndroid Build Coastguard Worker 50*d9e8da70SAndroid Build Coastguard WorkerWhen the count of retained objects reaches a threshold, LeakCanary dumps the Java heap into a `.hprof` file (a **heap dump**) stored onto the Android file system (see [Where does LeakCanary store heap dumps?](faq.md#where-does-leakcanary-store-heap-dumps)). Dumping the heap freezes the app for a short amount of time, during which LeakCanary displays the following toast: 51*d9e8da70SAndroid Build Coastguard Worker 52*d9e8da70SAndroid Build Coastguard Worker 53*d9e8da70SAndroid Build Coastguard Worker**Figure 2.** LeakCanary shows a [toast](https://developer.android.com/guide/topics/ui/notifiers/toasts) while dumping the heap. 54*d9e8da70SAndroid Build Coastguard Worker 55*d9e8da70SAndroid Build Coastguard Worker## 3. Analyzing the heap 56*d9e8da70SAndroid Build Coastguard Worker 57*d9e8da70SAndroid Build Coastguard WorkerLeakCanary parses the `.hprof` file using [Shark](shark.md) and locates the retained objects in that heap dump. 58*d9e8da70SAndroid Build Coastguard Worker 59*d9e8da70SAndroid Build Coastguard Worker 60*d9e8da70SAndroid Build Coastguard Worker**Figure 3.** LeakCanary finds retained objects in the heap dump. 61*d9e8da70SAndroid Build Coastguard Worker 62*d9e8da70SAndroid Build Coastguard WorkerFor each retained object, LeakCanary finds the path of references that prevents that retained object from being garbage collected: its **leak trace**. You will learn to analyze a leak trace in the next section: [Fixing a memory leak](fundamentals-fixing-a-memory-leak.md). 63*d9e8da70SAndroid Build Coastguard Worker 64*d9e8da70SAndroid Build Coastguard Worker 65*d9e8da70SAndroid Build Coastguard Worker**Figure 4.** LeakCanary computes the leak trace for each retained object. 66*d9e8da70SAndroid Build Coastguard Worker 67*d9e8da70SAndroid Build Coastguard WorkerWhen the analysis is done, LeakCanary displays a **notification** with a summary, and also prints the result in **Logcat**. Notice below how the **4 retained objects** are grouped as **2 distinct leaks**. LeakCanary creates a **signature for each leak trace**, and groups together leaks that have the same signature, ie leaks that are caused by the same bug. 68*d9e8da70SAndroid Build Coastguard Worker 69*d9e8da70SAndroid Build Coastguard Worker 70*d9e8da70SAndroid Build Coastguard Worker**Figure 5.** The 4 leak traces turned into 2 distinct leak signatures. 71*d9e8da70SAndroid Build Coastguard Worker 72*d9e8da70SAndroid Build Coastguard Worker 73*d9e8da70SAndroid Build Coastguard Worker``` 74*d9e8da70SAndroid Build Coastguard Worker==================================== 75*d9e8da70SAndroid Build Coastguard WorkerHEAP ANALYSIS RESULT 76*d9e8da70SAndroid Build Coastguard Worker==================================== 77*d9e8da70SAndroid Build Coastguard Worker2 APPLICATION LEAKS 78*d9e8da70SAndroid Build Coastguard Worker 79*d9e8da70SAndroid Build Coastguard WorkerDisplaying only 1 leak trace out of 2 with the same signature 80*d9e8da70SAndroid Build Coastguard WorkerSignature: ce9dee3a1feb859fd3b3a9ff51e3ddfd8efbc6 81*d9e8da70SAndroid Build Coastguard Worker┬─── 82*d9e8da70SAndroid Build Coastguard Worker│ GC Root: Local variable in native code 83*d9e8da70SAndroid Build Coastguard Worker│ 84*d9e8da70SAndroid Build Coastguard Worker... 85*d9e8da70SAndroid Build Coastguard Worker``` 86*d9e8da70SAndroid Build Coastguard Worker 87*d9e8da70SAndroid Build Coastguard WorkerTapping the notification starts an activity that provides more details. Come back to it again later by tapping the LeakCanary launcher icon: 88*d9e8da70SAndroid Build Coastguard Worker 89*d9e8da70SAndroid Build Coastguard Worker 90*d9e8da70SAndroid Build Coastguard Worker**Figure 6.** LeakCanary adds a launcher icon for each app it's installed in. 91*d9e8da70SAndroid Build Coastguard Worker 92*d9e8da70SAndroid Build Coastguard WorkerEach row corresponds to a **group of leaks with the same signature**. LeakCanary marks a row as <span style="border-radius: 20px; background: #ffd24c; padding-left: 8px; padding-right: 8px; padding-top: 2px; padding-bottom: 2px; color: #141c1f;">New</span> the first time the app triggers a leak with that signature. 93*d9e8da70SAndroid Build Coastguard Worker 94*d9e8da70SAndroid Build Coastguard Worker 95*d9e8da70SAndroid Build Coastguard Worker**Figure 7.** The 4 leaks grouped into 2 rows, one for each distinct leak signature. 96*d9e8da70SAndroid Build Coastguard Worker 97*d9e8da70SAndroid Build Coastguard WorkerTap on a leak to open up a screen with the leak trace. You can toggle between retained objects and their leak trace via a drop down. 98*d9e8da70SAndroid Build Coastguard Worker 99*d9e8da70SAndroid Build Coastguard Worker 100*d9e8da70SAndroid Build Coastguard Worker**Figure 8.** A screen showing 3 leaks grouped by their common leak signature. 101*d9e8da70SAndroid Build Coastguard Worker 102*d9e8da70SAndroid Build Coastguard WorkerThe **leak signature** is the **hash of the concatenation of each <span style="color: #9976a8;">reference</span> suspected to cause the leak**, ie each reference **<span style="text-decoration: underline; text-decoration-color: red; text-decoration-style: wavy; color: #9976a8;">displayed with a red underline</span>**: 103*d9e8da70SAndroid Build Coastguard Worker 104*d9e8da70SAndroid Build Coastguard Worker 105*d9e8da70SAndroid Build Coastguard Worker**Figure 9.** A leak trace with 3 suspect references. 106*d9e8da70SAndroid Build Coastguard Worker 107*d9e8da70SAndroid Build Coastguard WorkerThese same suspicious references are underlined with `~~~` when the leak trace is shared as text: 108*d9e8da70SAndroid Build Coastguard Worker 109*d9e8da70SAndroid Build Coastguard Worker``` 110*d9e8da70SAndroid Build Coastguard Worker... 111*d9e8da70SAndroid Build Coastguard Worker│ 112*d9e8da70SAndroid Build Coastguard Worker├─ com.example.leakcanary.LeakingSingleton class 113*d9e8da70SAndroid Build Coastguard Worker│ Leaking: NO (a class is never leaking) 114*d9e8da70SAndroid Build Coastguard Worker│ ↓ static LeakingSingleton.leakedViews 115*d9e8da70SAndroid Build Coastguard Worker│ ~~~~~~~~~~~ 116*d9e8da70SAndroid Build Coastguard Worker├─ java.util.ArrayList instance 117*d9e8da70SAndroid Build Coastguard Worker│ Leaking: UNKNOWN 118*d9e8da70SAndroid Build Coastguard Worker│ ↓ ArrayList.elementData 119*d9e8da70SAndroid Build Coastguard Worker│ ~~~~~~~~~~~ 120*d9e8da70SAndroid Build Coastguard Worker├─ java.lang.Object[] array 121*d9e8da70SAndroid Build Coastguard Worker│ Leaking: UNKNOWN 122*d9e8da70SAndroid Build Coastguard Worker│ ↓ Object[].[0] 123*d9e8da70SAndroid Build Coastguard Worker│ ~~~ 124*d9e8da70SAndroid Build Coastguard Worker├─ android.widget.TextView instance 125*d9e8da70SAndroid Build Coastguard Worker│ Leaking: YES (View.mContext references a destroyed activity) 126*d9e8da70SAndroid Build Coastguard Worker... 127*d9e8da70SAndroid Build Coastguard Worker``` 128*d9e8da70SAndroid Build Coastguard Worker 129*d9e8da70SAndroid Build Coastguard WorkerIn the example above, the signature of the leak would be computed as: 130*d9e8da70SAndroid Build Coastguard Worker 131*d9e8da70SAndroid Build Coastguard Worker```kotlin 132*d9e8da70SAndroid Build Coastguard Workerval leakSignature = sha1Hash( 133*d9e8da70SAndroid Build Coastguard Worker "com.example.leakcanary.LeakingSingleton.leakedView" + 134*d9e8da70SAndroid Build Coastguard Worker "java.util.ArrayList.elementData" + 135*d9e8da70SAndroid Build Coastguard Worker "java.lang.Object[].[x]" 136*d9e8da70SAndroid Build Coastguard Worker) 137*d9e8da70SAndroid Build Coastguard Workerprintln(leakSignature) 138*d9e8da70SAndroid Build Coastguard Worker// dbfa277d7e5624792e8b60bc950cd164190a11aa 139*d9e8da70SAndroid Build Coastguard Worker``` 140*d9e8da70SAndroid Build Coastguard Worker 141*d9e8da70SAndroid Build Coastguard Worker## 4. Categorizing leaks 142*d9e8da70SAndroid Build Coastguard Worker 143*d9e8da70SAndroid Build Coastguard WorkerLeakCanary separates the leaks it finds in your app into two categories: **Application Leaks** and **Library Leaks**. A **Library Leak** is a leak caused by a known bug in 3rd party code that you do not have control over. This leak is impacting your application, but unfortunately fixing it may not be in your control so LeakCanary separates it out. 144*d9e8da70SAndroid Build Coastguard Worker 145*d9e8da70SAndroid Build Coastguard WorkerThe two categories are separated in the result printed in **Logcat**: 146*d9e8da70SAndroid Build Coastguard Worker 147*d9e8da70SAndroid Build Coastguard Worker``` 148*d9e8da70SAndroid Build Coastguard Worker==================================== 149*d9e8da70SAndroid Build Coastguard WorkerHEAP ANALYSIS RESULT 150*d9e8da70SAndroid Build Coastguard Worker==================================== 151*d9e8da70SAndroid Build Coastguard Worker0 APPLICATION LEAKS 152*d9e8da70SAndroid Build Coastguard Worker 153*d9e8da70SAndroid Build Coastguard Worker==================================== 154*d9e8da70SAndroid Build Coastguard Worker1 LIBRARY LEAK 155*d9e8da70SAndroid Build Coastguard Worker 156*d9e8da70SAndroid Build Coastguard Worker... 157*d9e8da70SAndroid Build Coastguard Worker┬─── 158*d9e8da70SAndroid Build Coastguard Worker│ GC Root: Local variable in native code 159*d9e8da70SAndroid Build Coastguard Worker│ 160*d9e8da70SAndroid Build Coastguard Worker... 161*d9e8da70SAndroid Build Coastguard Worker``` 162*d9e8da70SAndroid Build Coastguard Worker 163*d9e8da70SAndroid Build Coastguard WorkerLeakCanary marks a row as a <span style="border-radius: 20px; background: #4e462f; padding-left: 8px; padding-right: 8px; padding-top: 2px; padding-bottom: 2px; color: #ffcc32;">Library Leak</span> in its list of leaks: 164*d9e8da70SAndroid Build Coastguard Worker 165*d9e8da70SAndroid Build Coastguard Worker 166*d9e8da70SAndroid Build Coastguard Worker**Figure 10.** LeakCanary found a Library Leak. 167*d9e8da70SAndroid Build Coastguard Worker 168*d9e8da70SAndroid Build Coastguard WorkerLeakCanary ships with a database of known leaks, which it recognizes by pattern matching on reference names. For example: 169*d9e8da70SAndroid Build Coastguard Worker 170*d9e8da70SAndroid Build Coastguard Worker``` 171*d9e8da70SAndroid Build Coastguard WorkerLeak pattern: instance field android.app.Activity$1#this$0 172*d9e8da70SAndroid Build Coastguard WorkerDescription: Android Q added a new IRequestFinishCallback$Stub class [...] 173*d9e8da70SAndroid Build Coastguard Worker┬─── 174*d9e8da70SAndroid Build Coastguard Worker│ GC Root: Global variable in native code 175*d9e8da70SAndroid Build Coastguard Worker│ 176*d9e8da70SAndroid Build Coastguard Worker├─ android.app.Activity$1 instance 177*d9e8da70SAndroid Build Coastguard Worker│ Leaking: UNKNOWN 178*d9e8da70SAndroid Build Coastguard Worker│ Anonymous subclass of android.app.IRequestFinishCallback$Stub 179*d9e8da70SAndroid Build Coastguard Worker│ ↓ Activity$1.this$0 180*d9e8da70SAndroid Build Coastguard Worker│ ~~~~~~ 181*d9e8da70SAndroid Build Coastguard Worker╰→ com.example.MainActivity instance 182*d9e8da70SAndroid Build Coastguard Worker``` 183*d9e8da70SAndroid Build Coastguard Worker 184*d9e8da70SAndroid Build Coastguard Worker!!! quote "What did I do to cause this leak?" 185*d9e8da70SAndroid Build Coastguard Worker Nothing wrong! You used an API the way it was intended but the implementation has a bug that is causing this leak. 186*d9e8da70SAndroid Build Coastguard Worker 187*d9e8da70SAndroid Build Coastguard Worker!!! quote "Is there anything I can do to prevent it?" 188*d9e8da70SAndroid Build Coastguard Worker Maybe! Some Library Leaks can be fixed using reflection, others by exercising a code path that makes the leak go away. This type of fix tends to be hacky, so beware! Your best option might be to find the bug report or file one, and insist that the bug gets fixed. 189*d9e8da70SAndroid Build Coastguard Worker 190*d9e8da70SAndroid Build Coastguard Worker!!! quote "Since I can't do much about this leak, is there a way I can ask LeakCanary to ignore it?" 191*d9e8da70SAndroid Build Coastguard Worker There's no way for LeakCanary to know whether a leak is a Library Leak prior to dumping the heap and analyzing it. If LeakCanary didn't show the result notification when a Library Leak is found then you'd start wondering what happened to the LeakCanary analysis after the dumping toast. 192*d9e8da70SAndroid Build Coastguard Worker 193*d9e8da70SAndroid Build Coastguard WorkerYou can see the full list of known leaks in the [AndroidReferenceMatchers](https://github.com/square/leakcanary/blob/main/shark-android/src/main/java/shark/AndroidReferenceMatchers.kt#L49) class. If you find an Android SDK leak that isn't recognized, please [report it](faq.md#can-a-leak-be-caused-by-the-android-sdk). You can also [customize the list of known Library Leaks](recipes.md#matching-known-library-leaks). 194*d9e8da70SAndroid Build Coastguard Worker 195*d9e8da70SAndroid Build Coastguard WorkerWhat's next? Learn how to [fix a memory leak](fundamentals-fixing-a-memory-leak.md)! 196