LeakCanary is introduced:

LeakCanary is an Android memory leak detection tool that helps developers resolve OOM crashes. Github address: github.com/square/leak…

LeakCanary access:

Add a line in the module of the build. Gradle debugImplementation ‘com. Squareup. Leakcanary: leakcanary – android: 2.7’ yes, only need to add this line is finished, Other initialization operations will be performed automatically in the LeakCanary library.

Use of Leak Canary:

LeakCanary: leakCanary is running and ready to detect Leaks

Once LeakCanary is installed, it automatically detects and reports a memory leak through the following four steps.

  1. Detect reserved objects,
  2. Dump stack
  3. Analysis of the stack
  4. Classify spills

1. Check reserved objects

LeakCanary hooks the Android lifecycle to automatically detect memory leaks. When activities and fragments are destroyed and need to be recycled, these destroyed objects are passed to the ObjectWatcher. ObjectWatcher holds weak references to these objects, and LeakCanary automatically detects if any of the following objects have leaked.

  • (1). Destroyed Activity instances
  • (2). Fragment instances that have been destroyed
  • (3). Destroyed small View instances
  • (4). Empty ViewModel instance

2. The heap dump

Retained object, LeakCanary dumps dumps the Java heap into a. Hprof file. The Dump temporarily freezes the app and presents a Toast.

3. Analyze the heap

LeakCanary parses the. Hprof file using Shark to locate the retained heap object in the heap.

4. Classify leaks

When the analysis is complete, LeakCanary shows an overview notification that LeakCanary creates a signature for each leak, and leaks in the same group have the same signature, their leaks are caused by the same problem and are classified in the same category.

Fix memory leaks:

Follow these four steps to resolve memory leaks:

  1. Find traces of memory leaks
  2. Narrow down the list of suspected references
  3. Find the reference that caused the leak
  4. Repair leaks

LeakCanary helps you complete the first two steps and the second two on your own.

1. Look for leaks

The memory leak path is a reference path from the garbage collection root node to a retained object. This reference holds an object in memory, thus preventing the garbage collector from collecting it.

For example, let’s save a healper singleton in a static variable. It then tells LeakCanary that this singleton is expected to be recycled, which of course is not, so a memory leak is faked. AppWatcher.objectWatcher.watch(Utils.helper)

`class Helper { }

class Utils { public static Helper helper = new Helper(); } `

The leak path for this singleton is ┬─── GC Root: A Local variable in native code │ ├ ─ dalvik. System. PathClassLoader instance │ left PathClassLoader. RuntimeInternalObjects ├ ─ Java.lang.Object[] array │ ↓ Object[].[43] ├─ Com.example.utils class │ ↓ Static Utils. Helper ╰→ Java.example.helper

Let’s examine it. At the top, the OathClassLoader instance is held by the GC Root. GCroot is a special object that is always reachable

  • A local variable in the thread stack
  • An active thread object
  • System classes that cannot be unloaded
  • Native code reference

Lines starting with ├─ represent Java objects, and lines starting with │ ↓ represent references to the next Java object. The PathClassLoader has a runtimeInternalObjects property, which is an array of objects. The 43rd reference of the array points to the Utils class ╰→ The first line points to the leaked object, and the Utils has a static helper pointing to the leaked object.

2. Narrow down suspicious references

LeakCanary is a reference path, and multiple paths in the path are suspected of causing the leak. This automatically Narrows down the LeakCanary, allowing us to quickly find the cause of the memory leak.

There is the following code ‘class ExampleApplication: Application() {val leakedViews = mutableListOf()}

class MainActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main_activity)

val textView = findViewById<View>(R.id.helper_text)

val app = application as ExampleApplication
// This creates a leak, What a Terrible Failure!
app.leakedViews.add(textView)
Copy the code

} }`

LeakCanary provides the following leak path ┬── GC Root: The System class │ ├ ─ android. The provider. FontsContract class │ left static FontsContract. SContext ├ ─ Com. Example. Leakcanary. ExampleApplication instance │ left ExampleApplication. LeakedViews ├ ─ Java. Util. ArrayList instance │ ↓ ArrayList.ElementData ├─ Java.lang.Object[] Array │ ├─ Android.widget.textView Instance │ ↓ The TextView. MContext ╰ – com. Example. Leakcanary. MainActivity instance to analyze the path: The FontsContract class is a system class that has a sContext static property that points to the ExampleApplication object, which has a leakedViews that points to an ArrayList that has an object that references TextView, It holds a Context that points to an activity that has been destory

In android apps, Application is a singleton that is not recycled,

3. Find the quote that caused the leak

Based on previous analysis, it was found that the reference that caused the leak was the Application holding the View in the destroyed activity

4. Resolve memory leaks

Modify according to the actual situation.