xref: /aosp_15_r20/external/leakcanary2/docs/fundamentals-fixing-a-memory-leak.md (revision d9e8da70d8c9df9a41d7848ae506fb3115cae6e6)
1*d9e8da70SAndroid Build Coastguard WorkerA memory leak is a programming error that causes an application to keep a reference to an object that is no longer needed. Somewhere in the code, there's a reference that should have been cleared and wasn't.
2*d9e8da70SAndroid Build Coastguard Worker
3*d9e8da70SAndroid Build Coastguard WorkerFollow these 4 steps to fix memory leaks:
4*d9e8da70SAndroid Build Coastguard Worker
5*d9e8da70SAndroid Build Coastguard Worker1. Find the leak trace.
6*d9e8da70SAndroid Build Coastguard Worker2. Narrow down the suspect references.
7*d9e8da70SAndroid Build Coastguard Worker3. Find the reference causing the leak.
8*d9e8da70SAndroid Build Coastguard Worker4. Fix the leak.
9*d9e8da70SAndroid Build Coastguard Worker
10*d9e8da70SAndroid Build Coastguard WorkerLeakCanary helps you with the first two steps. The last two steps are up to you!
11*d9e8da70SAndroid Build Coastguard Worker
12*d9e8da70SAndroid Build Coastguard Worker## 1. Find the leak trace
13*d9e8da70SAndroid Build Coastguard Worker
14*d9e8da70SAndroid Build Coastguard WorkerA **leak trace** is a shorter name for the *best strong reference path from garbage collection roots to the retained object*, ie the path of references that is holding an object in memory, therefore preventing it from being garbage collected.
15*d9e8da70SAndroid Build Coastguard Worker
16*d9e8da70SAndroid Build Coastguard WorkerFor example, let's store a helper singleton in a static field:
17*d9e8da70SAndroid Build Coastguard Worker
18*d9e8da70SAndroid Build Coastguard Worker```java
19*d9e8da70SAndroid Build Coastguard Workerclass Helper {
20*d9e8da70SAndroid Build Coastguard Worker}
21*d9e8da70SAndroid Build Coastguard Worker
22*d9e8da70SAndroid Build Coastguard Workerclass Utils {
23*d9e8da70SAndroid Build Coastguard Worker  public static Helper helper = new Helper();
24*d9e8da70SAndroid Build Coastguard Worker}
25*d9e8da70SAndroid Build Coastguard Worker```
26*d9e8da70SAndroid Build Coastguard Worker
27*d9e8da70SAndroid Build Coastguard WorkerLet's tell LeakCanary that the singleton instance is expected to be garbage collected:
28*d9e8da70SAndroid Build Coastguard Worker
29*d9e8da70SAndroid Build Coastguard Worker```
30*d9e8da70SAndroid Build Coastguard WorkerAppWatcher.objectWatcher.watch(Utils.helper)
31*d9e8da70SAndroid Build Coastguard Worker```
32*d9e8da70SAndroid Build Coastguard Worker
33*d9e8da70SAndroid Build Coastguard WorkerThe leak trace for that singleton looks like this:
34*d9e8da70SAndroid Build Coastguard Worker
35*d9e8da70SAndroid Build Coastguard Worker```
36*d9e8da70SAndroid Build Coastguard Worker┬───
37*d9e8da70SAndroid Build Coastguard Worker│ GC Root: Local variable in native code
38*d9e8da70SAndroid Build Coastguard Worker39*d9e8da70SAndroid Build Coastguard Worker├─ dalvik.system.PathClassLoader instance
40*d9e8da70SAndroid Build Coastguard Worker│    ↓ PathClassLoader.runtimeInternalObjects
41*d9e8da70SAndroid Build Coastguard Worker├─ java.lang.Object[] array
42*d9e8da70SAndroid Build Coastguard Worker│    ↓ Object[].[43]
43*d9e8da70SAndroid Build Coastguard Worker├─ com.example.Utils class
44*d9e8da70SAndroid Build Coastguard Worker│    ↓ static Utils.helper
45*d9e8da70SAndroid Build Coastguard Worker╰→ java.example.Helper
46*d9e8da70SAndroid Build Coastguard Worker```
47*d9e8da70SAndroid Build Coastguard Worker
48*d9e8da70SAndroid Build Coastguard WorkerLet's break it down! At the top, a `PathClassLoader` instance is held by a **garbage collection (GC) root**, more specifically a local variable in native code. GC roots are special objects that are always reachable, ie they cannot be garbage collected. There are 4 main types of GC root:
49*d9e8da70SAndroid Build Coastguard Worker
50*d9e8da70SAndroid Build Coastguard Worker* **Local variables**, which belong to the stack of a thread.
51*d9e8da70SAndroid Build Coastguard Worker* Instances of **active Java threads**.
52*d9e8da70SAndroid Build Coastguard Worker* **System Classes**, which never unload.
53*d9e8da70SAndroid Build Coastguard Worker* **Native references**, which are controlled by native code.
54*d9e8da70SAndroid Build Coastguard Worker
55*d9e8da70SAndroid Build Coastguard Worker```
56*d9e8da70SAndroid Build Coastguard Worker┬───
57*d9e8da70SAndroid Build Coastguard Worker│ GC Root: Local variable in native code
58*d9e8da70SAndroid Build Coastguard Worker59*d9e8da70SAndroid Build Coastguard Worker├─ dalvik.system.PathClassLoader instance
60*d9e8da70SAndroid Build Coastguard Worker```
61*d9e8da70SAndroid Build Coastguard Worker
62*d9e8da70SAndroid Build Coastguard WorkerA line starting with `├─ ` represents a Java object (either a class, an object array or an instance), and a line starting with `│    ↓ ` represents a reference to the Java object on the next line.
63*d9e8da70SAndroid Build Coastguard Worker
64*d9e8da70SAndroid Build Coastguard Worker`PathClassLoader` has a `runtimeInternalObjects` field that is a reference to an array of `Object`:
65*d9e8da70SAndroid Build Coastguard Worker
66*d9e8da70SAndroid Build Coastguard Worker```
67*d9e8da70SAndroid Build Coastguard Worker├─ dalvik.system.PathClassLoader instance
68*d9e8da70SAndroid Build Coastguard Worker│    ↓ PathClassLoader.runtimeInternalObjects
69*d9e8da70SAndroid Build Coastguard Worker├─ java.lang.Object[] array
70*d9e8da70SAndroid Build Coastguard Worker```
71*d9e8da70SAndroid Build Coastguard Worker
72*d9e8da70SAndroid Build Coastguard WorkerThe element at position 43 in that array of `Object` is a reference to the `Utils` class.
73*d9e8da70SAndroid Build Coastguard Worker
74*d9e8da70SAndroid Build Coastguard Worker```
75*d9e8da70SAndroid Build Coastguard Worker├─ java.lang.Object[] array
76*d9e8da70SAndroid Build Coastguard Worker│    ↓ Object[].[43]
77*d9e8da70SAndroid Build Coastguard Worker├─ com.example.Utils class
78*d9e8da70SAndroid Build Coastguard Worker```
79*d9e8da70SAndroid Build Coastguard Worker
80*d9e8da70SAndroid Build Coastguard WorkerA line starting with `╰→ ` represents the leaking object, ie the object that is passed to [AppWatcher.objectWatcher.watch()](/leakcanary/api/leakcanary-object-watcher-android/leakcanary/-app-watcher/object-watcher/).
81*d9e8da70SAndroid Build Coastguard Worker
82*d9e8da70SAndroid Build Coastguard WorkerThe `Utils` class has a static `helper` field which is a reference to the leaking object, which is the Helper singleton instance:
83*d9e8da70SAndroid Build Coastguard Worker
84*d9e8da70SAndroid Build Coastguard Worker```
85*d9e8da70SAndroid Build Coastguard Worker├─ com.example.Utils class
86*d9e8da70SAndroid Build Coastguard Worker│    ↓ static Utils.helper
87*d9e8da70SAndroid Build Coastguard Worker╰→ java.example.Helper instance
88*d9e8da70SAndroid Build Coastguard Worker```
89*d9e8da70SAndroid Build Coastguard Worker
90*d9e8da70SAndroid Build Coastguard Worker## 2. Narrow down the suspect references
91*d9e8da70SAndroid Build Coastguard Worker
92*d9e8da70SAndroid Build Coastguard WorkerA leak trace is a path of references. Initially, all references in that path are suspected of causing the leak, but LeakCanary can automatically narrow down the suspect references. To understand what that means, let's go through that process manually.
93*d9e8da70SAndroid Build Coastguard Worker
94*d9e8da70SAndroid Build Coastguard WorkerHere's an example of bad Android code:
95*d9e8da70SAndroid Build Coastguard Worker
96*d9e8da70SAndroid Build Coastguard Worker```kotlin
97*d9e8da70SAndroid Build Coastguard Workerclass ExampleApplication : Application() {
98*d9e8da70SAndroid Build Coastguard Worker  val leakedViews = mutableListOf<View>()
99*d9e8da70SAndroid Build Coastguard Worker}
100*d9e8da70SAndroid Build Coastguard Worker
101*d9e8da70SAndroid Build Coastguard Workerclass MainActivity : Activity() {
102*d9e8da70SAndroid Build Coastguard Worker  override fun onCreate(savedInstanceState: Bundle?) {
103*d9e8da70SAndroid Build Coastguard Worker    super.onCreate(savedInstanceState)
104*d9e8da70SAndroid Build Coastguard Worker    setContentView(R.layout.main_activity)
105*d9e8da70SAndroid Build Coastguard Worker
106*d9e8da70SAndroid Build Coastguard Worker	val textView = findViewById<View>(R.id.helper_text)
107*d9e8da70SAndroid Build Coastguard Worker
108*d9e8da70SAndroid Build Coastguard Worker    val app = application as ExampleApplication
109*d9e8da70SAndroid Build Coastguard Worker	// This creates a leak, What a Terrible Failure!
110*d9e8da70SAndroid Build Coastguard Worker	app.leakedViews.add(textView)
111*d9e8da70SAndroid Build Coastguard Worker  }
112*d9e8da70SAndroid Build Coastguard Worker}
113*d9e8da70SAndroid Build Coastguard Worker```
114*d9e8da70SAndroid Build Coastguard Worker
115*d9e8da70SAndroid Build Coastguard WorkerLeakCanary produces a leak trace that looks like this:
116*d9e8da70SAndroid Build Coastguard Worker
117*d9e8da70SAndroid Build Coastguard Worker```
118*d9e8da70SAndroid Build Coastguard Worker┬───
119*d9e8da70SAndroid Build Coastguard Worker│ GC Root: System class
120*d9e8da70SAndroid Build Coastguard Worker121*d9e8da70SAndroid Build Coastguard Worker├─ android.provider.FontsContract class
122*d9e8da70SAndroid Build Coastguard Worker│    ↓ static FontsContract.sContext
123*d9e8da70SAndroid Build Coastguard Worker├─ com.example.leakcanary.ExampleApplication instance
124*d9e8da70SAndroid Build Coastguard Worker│    ↓ ExampleApplication.leakedViews
125*d9e8da70SAndroid Build Coastguard Worker├─ java.util.ArrayList instance
126*d9e8da70SAndroid Build Coastguard Worker│    ↓ ArrayList.elementData
127*d9e8da70SAndroid Build Coastguard Worker├─ java.lang.Object[] array
128*d9e8da70SAndroid Build Coastguard Worker│    ↓ Object[].[0]
129*d9e8da70SAndroid Build Coastguard Worker├─ android.widget.TextView instance
130*d9e8da70SAndroid Build Coastguard Worker│    ↓ TextView.mContext
131*d9e8da70SAndroid Build Coastguard Worker╰→ com.example.leakcanary.MainActivity instance
132*d9e8da70SAndroid Build Coastguard Worker```
133*d9e8da70SAndroid Build Coastguard Worker
134*d9e8da70SAndroid Build Coastguard WorkerHere's how to read that leak trace:
135*d9e8da70SAndroid Build Coastguard Worker
136*d9e8da70SAndroid Build Coastguard Worker> The `FontsContract` class is a system class (see `GC Root: System class`) and has an `sContext` static field which references an `ExampleApplication` instance which has a `leakedViews` field which references an `ArrayList` instance which references an array (the array backing the array list implementation) which has an element that references a `TextView` which has an `mContext` field which references a destroyed instance of `MainActivity`.
137*d9e8da70SAndroid Build Coastguard Worker
138*d9e8da70SAndroid Build Coastguard WorkerLeakCanary highlights all references suspected of causing this leak using ~~~ underlines. Initially, all references are suspect:
139*d9e8da70SAndroid Build Coastguard Worker
140*d9e8da70SAndroid Build Coastguard Worker```
141*d9e8da70SAndroid Build Coastguard Worker┬───
142*d9e8da70SAndroid Build Coastguard Worker│ GC Root: System class
143*d9e8da70SAndroid Build Coastguard Worker144*d9e8da70SAndroid Build Coastguard Worker├─ android.provider.FontsContract class
145*d9e8da70SAndroid Build Coastguard Worker│    ↓ static FontsContract.sContext
146*d9e8da70SAndroid Build Coastguard Worker│                           ~~~~~~~~
147*d9e8da70SAndroid Build Coastguard Worker├─ com.example.leakcanary.ExampleApplication instance
148*d9e8da70SAndroid Build Coastguard Worker│    Leaking: NO (Application is a singleton)
149*d9e8da70SAndroid Build Coastguard Worker│    ↓ ExampleApplication.leakedViews
150*d9e8da70SAndroid Build Coastguard Worker│                         ~~~~~~~~~~~
151*d9e8da70SAndroid Build Coastguard Worker├─ java.util.ArrayList instance
152*d9e8da70SAndroid Build Coastguard Worker│    ↓ ArrayList.elementData
153*d9e8da70SAndroid Build Coastguard Worker│                ~~~~~~~~~~~
154*d9e8da70SAndroid Build Coastguard Worker├─ java.lang.Object[] array
155*d9e8da70SAndroid Build Coastguard Worker│    ↓ Object[].[0]
156*d9e8da70SAndroid Build Coastguard Worker│               ~~~
157*d9e8da70SAndroid Build Coastguard Worker├─ android.widget.TextView instance
158*d9e8da70SAndroid Build Coastguard Worker│    ↓ TextView.mContext
159*d9e8da70SAndroid Build Coastguard Worker│               ~~~~~~~~
160*d9e8da70SAndroid Build Coastguard Worker╰→ com.example.leakcanary.MainActivity instance
161*d9e8da70SAndroid Build Coastguard Worker```
162*d9e8da70SAndroid Build Coastguard Worker
163*d9e8da70SAndroid Build Coastguard WorkerThen, LeakCanary makes deductions about the **state** and the **lifecycle** of the objects in the leak trace. In an Android app the `Application` instance is a singleton that is never garbage collected, so it's never leaking (`Leaking: NO (Application is a singleton)`). From that, LeakCanary concludes that the leak is not caused by `FontsContract.sContext` (removal of corresponding `~~~`). Here's the updated leak trace:
164*d9e8da70SAndroid Build Coastguard Worker
165*d9e8da70SAndroid Build Coastguard Worker```
166*d9e8da70SAndroid Build Coastguard Worker┬───
167*d9e8da70SAndroid Build Coastguard Worker│ GC Root: System class
168*d9e8da70SAndroid Build Coastguard Worker169*d9e8da70SAndroid Build Coastguard Worker├─ android.provider.FontsContract class
170*d9e8da70SAndroid Build Coastguard Worker│    ↓ static FontsContract.sContext
171*d9e8da70SAndroid Build Coastguard Worker├─ com.example.leakcanary.ExampleApplication instance
172*d9e8da70SAndroid Build Coastguard Worker│    Leaking: NO (Application is a singleton)
173*d9e8da70SAndroid Build Coastguard Worker│    ↓ ExampleApplication.leakedViews
174*d9e8da70SAndroid Build Coastguard Worker│                         ~~~~~~~~~~~
175*d9e8da70SAndroid Build Coastguard Worker├─ java.util.ArrayList instance
176*d9e8da70SAndroid Build Coastguard Worker│    ↓ ArrayList.elementData
177*d9e8da70SAndroid Build Coastguard Worker│                ~~~~~~~~~~~
178*d9e8da70SAndroid Build Coastguard Worker├─ java.lang.Object[] array
179*d9e8da70SAndroid Build Coastguard Worker│    ↓ Object[].[0]
180*d9e8da70SAndroid Build Coastguard Worker│               ~~~
181*d9e8da70SAndroid Build Coastguard Worker├─ android.widget.TextView instance
182*d9e8da70SAndroid Build Coastguard Worker│    ↓ TextView.mContext
183*d9e8da70SAndroid Build Coastguard Worker│               ~~~~~~~~
184*d9e8da70SAndroid Build Coastguard Worker╰→ com.example.leakcanary.MainActivity instance
185*d9e8da70SAndroid Build Coastguard Worker```
186*d9e8da70SAndroid Build Coastguard Worker
187*d9e8da70SAndroid Build Coastguard WorkerThe `TextView` instance references the destroyed `MainActivity` instance via it's `mContext` field. Views should not survive the lifecycle of their context, so LeakCanary knows that this `TextView` instance is leaking (`Leaking: YES (View.mContext references a destroyed activity)`), and therefore that the leak is not caused by `TextView.mContext` (removal of corresponding `~~~`). Here's the updated leak trace:
188*d9e8da70SAndroid Build Coastguard Worker
189*d9e8da70SAndroid Build Coastguard Worker```
190*d9e8da70SAndroid Build Coastguard Worker┬───
191*d9e8da70SAndroid Build Coastguard Worker│ GC Root: System class
192*d9e8da70SAndroid Build Coastguard Worker193*d9e8da70SAndroid Build Coastguard Worker├─ android.provider.FontsContract class
194*d9e8da70SAndroid Build Coastguard Worker│    ↓ static FontsContract.sContext
195*d9e8da70SAndroid Build Coastguard Worker├─ com.example.leakcanary.ExampleApplication instance
196*d9e8da70SAndroid Build Coastguard Worker│    Leaking: NO (Application is a singleton)
197*d9e8da70SAndroid Build Coastguard Worker│    ↓ ExampleApplication.leakedViews
198*d9e8da70SAndroid Build Coastguard Worker│                         ~~~~~~~~~~~
199*d9e8da70SAndroid Build Coastguard Worker├─ java.util.ArrayList instance
200*d9e8da70SAndroid Build Coastguard Worker│    ↓ ArrayList.elementData
201*d9e8da70SAndroid Build Coastguard Worker│                ~~~~~~~~~~~
202*d9e8da70SAndroid Build Coastguard Worker├─ java.lang.Object[] array
203*d9e8da70SAndroid Build Coastguard Worker│    ↓ Object[].[0]
204*d9e8da70SAndroid Build Coastguard Worker│               ~~~
205*d9e8da70SAndroid Build Coastguard Worker├─ android.widget.TextView instance
206*d9e8da70SAndroid Build Coastguard Worker│    Leaking: YES (View.mContext references a destroyed activity)
207*d9e8da70SAndroid Build Coastguard Worker│    ↓ TextView.mContext
208*d9e8da70SAndroid Build Coastguard Worker╰→ com.example.leakcanary.MainActivity instance
209*d9e8da70SAndroid Build Coastguard Worker```
210*d9e8da70SAndroid Build Coastguard Worker
211*d9e8da70SAndroid Build Coastguard WorkerTo summarize, LeakCanary inspects the state of objects in the leak trace to figure out if these objects are leaking (`Leaking: YES` vs `Leaking: NO`), and leverages that information to narrow down the suspect references. You can provide custom `ObjectInspector` implementations to improve how LeakCanary works in your codebase (see [Identifying leaking objects and labeling objects](recipes.md#identifying-leaking-objects-and-labeling-objects)).
212*d9e8da70SAndroid Build Coastguard Worker
213*d9e8da70SAndroid Build Coastguard Worker## 3. Find the reference causing the leak
214*d9e8da70SAndroid Build Coastguard Worker
215*d9e8da70SAndroid Build Coastguard WorkerIn the previous example, LeakCanary narrowed down the suspect references to `ExampleApplication.leakedViews`, `ArrayList.elementData` and `Object[].[0]`:
216*d9e8da70SAndroid Build Coastguard Worker
217*d9e8da70SAndroid Build Coastguard Worker```
218*d9e8da70SAndroid Build Coastguard Worker┬───
219*d9e8da70SAndroid Build Coastguard Worker│ GC Root: System class
220*d9e8da70SAndroid Build Coastguard Worker221*d9e8da70SAndroid Build Coastguard Worker├─ android.provider.FontsContract class
222*d9e8da70SAndroid Build Coastguard Worker│    ↓ static FontsContract.sContext
223*d9e8da70SAndroid Build Coastguard Worker├─ com.example.leakcanary.ExampleApplication instance
224*d9e8da70SAndroid Build Coastguard Worker│    Leaking: NO (Application is a singleton)
225*d9e8da70SAndroid Build Coastguard Worker│    ↓ ExampleApplication.leakedViews
226*d9e8da70SAndroid Build Coastguard Worker│                         ~~~~~~~~~~~
227*d9e8da70SAndroid Build Coastguard Worker├─ java.util.ArrayList instance
228*d9e8da70SAndroid Build Coastguard Worker│    ↓ ArrayList.elementData
229*d9e8da70SAndroid Build Coastguard Worker│                ~~~~~~~~~~~
230*d9e8da70SAndroid Build Coastguard Worker├─ java.lang.Object[] array
231*d9e8da70SAndroid Build Coastguard Worker│    ↓ Object[].[0]
232*d9e8da70SAndroid Build Coastguard Worker│               ~~~
233*d9e8da70SAndroid Build Coastguard Worker├─ android.widget.TextView instance
234*d9e8da70SAndroid Build Coastguard Worker│    Leaking: YES (View.mContext references a destroyed activity)
235*d9e8da70SAndroid Build Coastguard Worker│    ↓ TextView.mContext
236*d9e8da70SAndroid Build Coastguard Worker╰→ com.example.leakcanary.MainActivity instance
237*d9e8da70SAndroid Build Coastguard Worker```
238*d9e8da70SAndroid Build Coastguard Worker
239*d9e8da70SAndroid Build Coastguard Worker`ArrayList.elementData` and `Object[].[0]` are implementation details of `ArrayList`, and it's unlikely that there's a bug in the `ArrayList` implementation, so the reference causing the leak is the only remaining reference: `ExampleApplication.leakedViews`.
240*d9e8da70SAndroid Build Coastguard Worker
241*d9e8da70SAndroid Build Coastguard Worker## 4. Fix the leak
242*d9e8da70SAndroid Build Coastguard Worker
243*d9e8da70SAndroid Build Coastguard WorkerOnce you find the reference causing the leak, you need to figure out what that reference is about, when it should have been cleared and why it hasn't been. Sometimes it's obvious, like in the previous example. Sometimes you need more information to figure it out. You can [add labels](recipes.md#identifying-leaking-objects-and-labeling-objects), or explore the hprof directly (see [How can I dig beyond the leak trace?](faq.md#how-can-i-dig-beyond-the-leak-trace)).
244*d9e8da70SAndroid Build Coastguard Worker
245*d9e8da70SAndroid Build Coastguard Worker
246*d9e8da70SAndroid Build Coastguard Worker!!! warning
247*d9e8da70SAndroid Build Coastguard Worker    Memory leaks cannot be fixed by replacing strong references with weak references. It's a common solution when attempting to quickly address memory issues, however it never works. The bugs that were causing references to be kept longer than necessary are still there. On top of that, it creates more bugs as some objects will now be garbage collected sooner than they should. It also makes the code much harder to maintain.
248*d9e8da70SAndroid Build Coastguard Worker
249*d9e8da70SAndroid Build Coastguard Worker
250*d9e8da70SAndroid Build Coastguard WorkerWhat's next? Customize LeakCanary to your needs with [code recipes](recipes.md)!
251