Leak Canary for Android performance optimization

Posted by robotta1530 on Sun, 02 Jan 2022 11:55:36 +0100

Introduction and use of LeakCanary for Android memory leak detection

Click on the official website here , this article is a rough translation of the official website and my own understanding

1, Use 🌚

To use LeakCanary, you need to add build. XML to the app module In the gradle file, add:

dependencies {
  	debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
}

Confirm that LeakCanary is running at startup. You can check whether there are the following outputs in the log cat:

D LeakCanary: LeakCanary is running and ready to detect leaks

At present, LeakCanary will automatically detect the following types of memory leaks:

- Destroyed Activity example
- Destroyed Fragment example
- Destroyed fargment view example
- Cleared ViewModel example

2, Introduction 🌘

LeakCanary is how the works and how to detect and fix memory leak bugs.

What is a memory leak?

In Java runtime, memory leakage means that a programming error causes an application to reference an Object that is no longer needed. As a result, the memory occupied by that Object cannot be recycled, resulting in OutOfMemoryError (OOM) and program crash

General causes of memory leaks

Most of the errors that cause memory leaks are related to the life cycle of the object. To name a few chestnuts:

  • Add a Fragment instance for standby, but it is not in Fragment Clear view fields in ondestoryview()
  • A Context field points to an Activity instance. When the application configuration changes (such as screen rotation), the referenced Activity instance will be rebuilt
  • register a listener, broadcast receiver, or RxJava subscription with an object that has a lifecycle. As a result, you forget to unregister at the end of the object lifecycle

3, How Canary Leary works 🌗

The work is divided into four steps:

  1. Detect legacy objects
  2. Dump heap memory
  3. Analytical reactor
  4. Memory leak detected

1. Detect legacy objects

LeakCanary uses hooks to weave into the android life cycle to automatically detect when activities and fragments are destroyed and recycled. These destroyed objects will be transferred to an Objectwatcher object in the form of weak references. You can see unwanted objects, such as the following inapplicable view objects:

AppWatcher.objectWatcher.watch(myDetachedView, "View was detached")

If the weak reference of Objectwatcher is not cleared after 5 seconds and garbage collection is run, the object is considered to be retained and may lead to memory leakage. And the following log will be printed:

D LeakCanary: Watching instance of com.example.leakcanary.MainActivity
  (Activity received Activity#onDestroy() callback) 

... 5 seconds later ...

D LeakCanary: Scheduling check for retained objects because found new object
  retained

LeakCanary waits until the reserved objects reach a threshold to transfer the heap memory, and will remind the current number with a notification. Clicking the notification will immediately transfer the heap memory. For threshold setting, the default threshold is 5 when applied in the foreground and 1 when applied in the background (invisible).

2. Heap memory transfer

As mentioned above, after the remaining objects reach the threshold, LeakCanary will convert the Java heap memory into a suffix of hprof files are stored in the file system, and Android is required for file storage permission. WRITE_ EXTERNAL_ The storage permission is stored in LeakCanary-com Example folder, where com Example is a package name you dare to use. In the process of saving, the application will pause for a while, and LeakCanary will give a prompt.

3. Analysis heap memory

Stored The hprof file will locate the remaining objects in memory through a library (this is not something we should care about). What we should care about is that for each legacy object, LeakCanary will find a reference path called leak trace that prevents the object from being recycled. After analysis, LeakCanary will print the results in Logcat, as shown below. LeakCanary will generate a signature for each leak trace, and then group the leaks according to the signature. Each group of leaks will generate the same error.

====================================
HEAP ANALYSIS RESULT
====================================
2 APPLICATION LEAKS

Displaying only 1 leak trace out of 2 with the same signature
Signature: ce9dee3a1feb859fd3b3a9ff51e3ddfd8efbc6
┬───
│ GC Root: Local variable in native code
│
...

LeakCanary will generate a startup icon in the application list. Each relevant reference will be marked with a red line below. When text sharing is used, it is similar to the following, marked with ~ ~ ~:

...
│  
├─ com.example.leakcanary.LeakingSingleton class
│    Leaking: NO (a class is never leaking)
│    ↓ static LeakingSingleton.leakedViews
│                              ~~~~~~~~~~~
├─ java.util.ArrayList instance
│    Leaking: UNKNOWN
│    ↓ ArrayList.elementData
│                ~~~~~~~~~~~
├─ java.lang.Object[] array
│    Leaking: UNKNOWN
│    ↓ Object[].[0]
│               ~~~
├─ android.widget.TextView instance
│    Leaking: YES (View.mContext references a destroyed activity)
...

4. Detect memory leaks

LeakCanary divides leaks into Application Leaks and Library Leaks. Memory leaks caused by third-party libraries can be identified and affect their own applications, but this problem is beyond our control. Of course, we can also use reflection or report such errors to the author.

4, Resolve memory leaks 🌖

This process is also divided into four steps:

1. find leak trace
2. Narrow the scope of suspected references
3. Find the cause of the leak
4. Fix at code level bug

1. Find leaked reference path

Introduce the concept of GC root. GC roots identifies objects that are always reachable, which means that they cannot be recycled. They mainly include the following four categories:

1,Local variable, belonging to a thread
2,Running Java Instance of thread
3,System class, never unloaded
4,The local method reference is controlled by the code of the local method

2. Narrow the scope of suspicion

In a leak path, all references are suspected objects at the beginning, and LeakCanary can automatically narrow the suspicious range. The specific process is to mark all reference objects first, and then detect them one by one from top to bottom on the path. The objects can be recycled. If it causes leakage, a sentence leading: YES will be added in front of the objects. The reason is as follows:

┬───
│ GC Root: System class
│
├─ android.provider.FontsContract class
│    ↓ static FontsContract.sContext
├─ com.example.leakcanary.ExampleApplication instance
│    Leaking: NO (Application is a singleton)
│    ↓ ExampleApplication.leakedViews
│                         ~~~~~~~~~~~
├─ java.util.ArrayList instance
│    ↓ ArrayList.elementData
│                ~~~~~~~~~~~
├─ java.lang.Object[] array
│    ↓ Object[].[0]
│               ~~~
├─ android.widget.TextView instance
│    Leaking: YES (View.mContext references a destroyed activity)
│    ↓ TextView.mContext
╰→ com.example.leakcanary.MainActivity instance

3. Find the reference that caused the leak according to the reduced scope

4. Fix errors when found 🌞

Topics: Android Memory Leak