android Leakcanary/Studio Profiler/MAT handles memory issues (leaks and Big objects)

1. Android Leakcanary use

1.1 Dependency in the project :

According to the actual situation of the project, select the corresponding version, because android x is not supported in the project, so the leakcanary 2.2 version is selected here.

    //放开下边的代码,即可在debug包中使用leakcanary 检测内存泄露
    debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.2'

1.2. Run the project to view memory leak information .

In logcat, you can view the leaked information:

2022-08-31 11:14:07.757 32255-10246/com.xxx.miniworld D/LeakCanary: ====================================
    //....
    1174715 bytes retained by leaking objects
    Signature: 4bfb4367c5b0b4b1a7aeb7ff3b45e02cba6a3d87
    ┬───
    │ GC Root: System class
    ├─ android.net.ConnectivityManager classLeaking: NO (a class is never leaking)
    │    ↓ static ConnectivityManager.sInstance~~~~~~~~~
    ├─ android.net.ConnectivityManager instanceLeaking: UNKNOWN
    │    ↓ ConnectivityManager.mContext~~~~~~~~
    ╰→ org.appplay.minibrowser.BrowserActivity instanceLeaking: YES (ObjectWatcher was watching this because org.appplay.minibrowser.BrowserActivity received Activity#onDestroy() callback and Activity#mDestroyed is true)
    ​     key = 2354b5c5-3110-4b61-986b-99e7012bf89a
    ​     watchDurationMillis = 107085
    ​     retainedDurationMillis = 102084
    ====================================
    //...
    Build.VERSION.SDK_INT: 31
    Build.MANUFACTURER: OPPO
    LeakCanary version: 2.2
    App process name: com.xxx.miniworld
    Analysis duration: 20828 ms
    Heap dump file path: /storage/emulated/0/Download/leakcanary-com.xxx.miniworld/2022-08-31_11-13-44_012.hprof
    Heap dump timestamp: 1661915647752
    ====================================

Interpret the above key information:

  • The memory occupation caused by the leaked object lock is 1174715 bytes, which is 1.12M.
  • Leaking: YES is a leak for this object
  • gc call chain: yes BrowserActivity被ConnectivityManager的mContext持有,而ConnectivityManager 类对象又是静态的, resulting in the failure of BrowserActivity to be released, resulting in memory leaks.
  • Heap dump file (that is, hprof memory snapshot file) storage: in /storage/emulated/0/Download/leakcanary-com.xxx.miniworldthe directory.

Of course, in addition to logcat, when LeakCanary detects a memory leak, a notification will pop up. We can click on the notification to view the leak information. I won’t introduce it here.

2. Android Studio view Hprof heap memory snapshot

By exporting the hprof file of the memory leak or the hprof file captured by the studio.

2.1 Information contained in the Hprof file :

  • What types of objects your app allocates, and how many of each.
  • How much memory each object is currently using.
  • Where in the code are the references to each object kept.
  • The call stack where the object was allocated

2.2 Studio opens the Hprof file

The way to load the hprof file: the bottom menu of the studio Profiler->SESSIONS->+号->load from a file->准备好的hprof文件.

First, interpret the tool interface options of the Hprof file to better understand the memory information.

The first option, looks at the specified heap :

insert image description here

  • app heap: the heap used by the current app
  • Image heap: The system boot image, which contains classes preloaded during boot. The assignment here ensures that it never moves or disappears
  • zgote heap: copy-on-write heap where application processes are forked from the Android system

Usually just focus on app heap and jni heap.

The second option, Select Assignment :

insert image description here

  • Arrange by class: Groups all assignments by class name. It's the default value
  • Arrange by package: Group all assignments by package name
  • Arrange by callstack: Groups all allocations to their corresponding callstack

Usually, select the "Arrange by package" option, so that you can find which objects are in memory in a more organized manner.

The third option, select the class to display :

insert image description here

  • show all classes: show all classes
  • show activity/fragment leak: show activity/fragment with memory leak
  • show project class: Show related classes in the project.

Interpretation in the class list in Hprof :

insert image description here

  • Class Name: class name
  • Allocations: The number of all objects of this class (the number of objects allocated by malloc() or new operator). If there are many, memory leaks can be considered
  • Native Size: The memory occupied by all objects of this class in the native layer (in bytes)
  • Shallow Size: The memory occupied by all objects of this class in the Java layer, excluding the memory of its referenced objects (in bytes)
  • Retained Size: All memory occupied by all objects of this class (including java and native layers, including the memory of its referenced objects)

For example, for the Bitmap class, there are 74 objects, 5043106 in the native layer, 3700 in the java layer, and 5049374 in all memory occupied by the Bitmap object.

2.3 Check the memory leak of Activity/Fragment :

Let's first understand the reasons that may cause memory leaks in Android :

  • Long-term references to Activity/Fragment/View/Drawable/other objects cause Fragment/Activity to be held and cannot be released.
  • The non-static inner class (Runnable) in the Activity, that is, the non-static inner class of the Activity is equivalent to the implicit outer class, which indirectly holds the Activity.
  • The object is cached/held for a time beyond the scope of use, such as a long-term cache of a large object in a singleton.

Next, first tap the third option, Select show activity/fragment leak. Then, select one of the activities and double-click, it will appear Instance List 项. Select one of the Instances and double-click to expand the Instance Detail details. Then click References, check show nearest gc root only, you can see the call chain of Activity/Fragment. As shown below:
insert image description here

First interpret the Instance View/Detail view :

  • Depth refers to the depth of the shortest path from the GC root node to the selected instance, which is simply the number of nodes in the call chain.
  • Native Size: The memory occupied by this object in the native layer (in bytes)
  • Shallow Size: The memory occupied by this object in the Java layer, excluding the memory of its reference object (in bytes)
  • Retained Size: The entire memory occupied by this object (including the java and native layers, including the memory of its referenced objects)

Further interpretation of Shallow and retained sizes
insert image description here
In the figure above, the Retained Size of obj1 is the sum of shallow sizes of (obj1+obj2+obj4). For more details, read Shallow and retained sizes

Note : The Retained Size here is different from the Retained Size in the class list. One is the representation of a single class object, and the other is the sum of all class objects.

Returning to the topic of memory leaks, according to the analysis of the call chain in the above figure, we can see that: 当 BrowserActivity被销毁时,BrowserActivity是作为ConnectivityManager的context属性被持有,没有被完全释放,从而导致内存泄漏.

Solution : Get various system severce objects through Application.

    /**
     * 处理leakcanary 检测到通过Activity创建system service, 容易被持有未释放
     * @param context
     * @return
     */
    private static Context getSafeContext(Context context){
        return context instanceof Application?context:context.getApplicationContext();
    }

In addition, right click jump to source to jump to the matching source code.

2.4 Check the leaks that may be caused by the occupation of other Java objects :

Look at the heap information of the Bitmap class, select the Retained Size flashback and find that the largest Bitmap object occupies 362947 (that is, 354k) of memory, and the call chain is a layout object in an Activity (indirectly holding the bitmap), which is not released, as shown below:

insert image description here

After sorting out the business, it is found that the layout is embedded in the Activity when the webview is loaded. Obviously, the holding time of the Layout object exceeds the display time.
So the solution: Release the layout object when displaying other services.

Look at the heap information of the String class, select the Retained Size flashback, and find that the largest String object occupies 168152 (that is, 164k) of memory. The call chain is cached in the hashMap in SharedPreferences and has not been released, as shown in the following figure:

insert image description here

Solution: Use Tencent's mmkv instead of android SharedPreferences or use file storage.

The occupation of other class objects is also a pot of painting, so I won't introduce them in detail here.

For more information, refer to the Android official capture heap dump

3. Use mat to analyze the hprof file

Early preparation

According to the computer and the installed jdk version, select the corresponding mat version. Download address: https://www.eclipse.org/mat/previousReleases.php

The hprof file of studio or Leakcanary cannot be opened by mat. Therefore, it is necessary to pass in android_sdk/platform-tools/hprof-convthe command to execute the conversion hprof-conv heap-original.hprof heap-converted.hprof. As shown below:
insert image description here

3.1 Leak Suspects view memory leaks

After opening the converted hprof memory snapshot file through mat, as shown in the figure below:

insert image description here
Click Leak Suspectsto view memory leaks:
insert image description here

It can be seen from the above that there are Class and String occupying a large amount of memory. Let's look at the String problem first, and click detail to view further details.

Let’s first understand the following key terms :

  • Shallow Heap: The memory occupied by the object does not include the object memory it refers to.
  • Retain Heap: After the object reference (including its reference object) is released, the memory that can be reduced.

Interpretation of List Object items :

  • with outgoing references refers to which object this object holds
  • with incoming references refers to which object holds the object

Let's first look at which objects hold String objects, 右键--> List objects-->with incoming referencesas shown in the following figure:

insert image description here
In list_object the view, you can clearly see which objects hold the string.

Next, look at the gc call path:

Select one of them, right click path to GCRoot -> exclude all phantom/weak/soft etc. references(remove all phantom references, weak references, soft references) and you will get the root node gc root that calls this class.
insert image description here
Next, enter the path2gc view:

insert image description here

As can be seen from the figure above, the string object is held by Hasmap in sharedPreferences, and sharedPreferences is held by a certain class and an array, and the string cannot be released.

Problems like this cannot be solved for the time being, and can only be skipped first. List_object视图中剩余的string 对象, is also a pot of painting, so I won’t introduce it in detail here.

Summary : View the steps of memory calls in class objects,先选择类对象->其次,哪个对象持有了该对象(右键--> List objects-->with incoming references)->最后,gc 调用路径( path to GCRoot -> exclude all phantom/weak/soft etc. references)

3.2 Find matches by Histogram search :

Search for the Activity keyword, and the search is as follows:
insert image description here

The current game has destroyed BrowserActivity, but it still exists in the memory snapshot. Obviously, the Activity leaks, check its gc call path (path to GCRoot -> exclude all phantom/weak/soft etc. references), as shown in the figure below:
insert image description here

It is found that BrowserActivity is held as the context attribute of ConnectivityManager and has not been completely released. This is the same as the above Studio Profiler analysis, and can be solved according to the above processing method.

In addition, you can also check whether there is a leak in the Fragment.

Search for the Bitmap keyword, and the search is shown in the figure below:
insert image description here

Check which objects hold the BitmapDrawable object, as shown in the following figure:

insert image description here

Select one of the BitmapDrawable's gc call path, as shown in the following figure:

insert image description here

It can be seen from the above figure that the BitmapDrawable object is held by the iBrowser object in BrowserActivity, and BrowserActivity is held by the context property of ConnectivityManager.

The problem is the same as above, and the solution is the same.

Usually, you can also retrieve the package name to see which objects in the business layer may have leaks, so I won’t go into too much detail here .

3.3. Dominator Tree (Dominator Tree) View large memory objects

In this view, the tree structure of each object (Object Instance) and its reference relationship is listed, and the size and percentage of occupied memory are also included, as shown in the following figure:

insert image description here

Sorting by Shallow Heap or Percentage (memory percentage), it is easy to filter out objects that occupy a large amount of memory, as shown below:
insert image description here

Of course, you can also choose the sorting method, as shown in the following figure:

insert image description here
Next, select objects that occupy a large amount of memory, check which objects are referenced or which objects are referenced, check the gc call path, and analyze how to optimize.

Guess you like

Origin blog.csdn.net/hexingen/article/details/126645702