English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Android Studio+MAT实战内存泄漏

For memory leaks, in Android, if not paid attention to, they are still relatively easy to occur, especially in Activities, which are more likely to occur. Below, I will talk about how I find memory leaks.

First, what is a memory leak?

Memory leaks refer to some unused objects that still exist in memory and cannot be recycled by the garbage collection mechanism, causing them to reside in memory constantly, which will cause the memory consumption to increase, eventually leading to poor program performance.
In the Android virtual machine, the root node search algorithm is used to enumerate root nodes to determine whether they are garbage. The virtual machine will traverse from GC Roots, and if a node cannot find a route to GC Roots, that is, it is not connected to GC Roots, then it proves that the reference is invalid and can be recycled. Memory leaks exist when some bad calls cause some unused objects to be connected to GC Roots and cannot be recycled.

Since we know what memory leaks are, we naturally know how to avoid them, which is to pay attention to long-time references to unused objects when writing code. It sounds simple, but it requires enough experience to achieve it, so memory leaks are relatively easy to occur. Since it is not easy to avoid completely, we must be able to find and fix memory leaks in the program.
Below, I will talk about how to find memory leaks.

Find memory leaks:

For example, the following code:

public class MainActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    String string = new String();
  }
  public void click(View view){
    Intent intent = new Intent();
    intent.setClass(getApplicationContext(), SecondActivity.class);
    startActivity(intent);
  }
}
public class SecondActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_second);
    Runnable runnable = new Runnable() {
      @Override
      public void run() {
        try {
          Thread.sleep(8000000L);
        catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    };
    new Thread(runnable).start();
  }
}

 

Each time the activity jumps to this Activity, a thread is called, and then this thread executes the run method of the runnable. Since Runnable is an anonymous inner object, it holds a reference to SecondActivity, so it is very simple for two activities, which can be jumped from MainActivity to SecondActivity, and then return to MainActivity, and do this continuously.5time, finally returning to MainActivity. According to common sense, after we return from SecondActivity to MainActivity, SecondActivity should be destroyed and recycled. However, in actuality, it may not be the case.

At this time, to judge whether a memory overflow has occurred, we need to use tools! There are two ways below

1Use MAT tool to find

Firstly, open the Android Device Monitor tool in AS, as shown in the following figure:


After opening it, the following interface will appear


First, select the package name of the application you want to detect, and then click the circled area in the following figure. An icon will be marked after the program package name


What we need to do next is to operate our app to jump back and forth5time.

After that, click the icon in the following figure to export the hprof file for analysis

The exported file is as shown in the following figure:

After obtaining the hprof file, we can use the MAT tool for analysis

Open the MAT tool

If not, you can download it from the following URL

MAT tool download address

The interface is as shown in the following figure:

Open the hprof file we exported earlier, and it is expected to report the following error

This is because MAT is used to analyze hprof files of java programs, and the hprof exported from Android has a certain format difference, so we need to convert the exported hprof file. The SDK provides us with the conversion tool hprof-conv.exe is located in the following figure


Next, we cd to this path and execute the command to convert our hprof file, as shown in the following figure


Among which hprof-How to use the conv command

hprof-conv source file output file

For example, hprof-conv E:\aaa.hprof E:\output.hprof

is to convert aaa.hprof to output.hprof for output. output.hprof is the file we have converted, as shown in the figure, mat2.hprof file is the file we have converted.

Next, we use the MAT tool to open the converted mat2file. After opening it without errors, as shown in the following figure:


After that, we can view the objects existing in the current memory. Since memory leaks generally occur in Activity, we only need to search for Activity.

Click the marked QQL icon in the following figure and enter 'select' * from instanceof android.app.Activity

Similar to SQL statements, find information related to Activity by clicking the red exclamation mark. As shown in the following figure:

Next, we can see the Activity information filtered out below

As shown in the figure above, there are still 6instances of SecondActivity, but we want to exit all of them, indicating that a memory leak has occurred

Among them, there are two properties: Shallow size and Retained Size

Shallow Size
The size of the memory occupied by the object itself, excluding the objects it references. For non-array type objects, its size is the sum of the object and all its member variables.
Of course, it will also include some data storage units of Java language features. For array type objects, its size is the sum of the sizes of the array element objects.
Retained Size
Retained Size=Size of the current object+The total size of objects that can be directly or indirectly referenced by the current object. (The meaning of indirect reference: A->B->C, C is indirect reference)
However, when releasing, we still need to exclude objects that are directly or indirectly referenced by GC Roots. They will not be considered as garbage temporarily.

Next, right-click on a SecondActivity


Select with all references

Open the page as shown in the following figure

View the page in the following figure

We can see that this0 references thisActivitywhilethis0 represents the meaning of an inner class, that is, an inner class references Activity and this$0 is referenced by target. Target is a thread, the reason has been found. The cause of memory leakage is that Activity is referenced by an inner class and the inner class is used by a thread, so it cannot be released. Let's turn to the code of this class to view it.

public class SecondActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_second);
    Runnable runnable = new Runnable() {
      @Override
      public void run() {
        try {
          Thread.sleep(8000000L);
        catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    };
    new Thread(runnable).start();
  }
}
Indeed, in

Indeed, there is a Runnable inner class object in SecondActivity, and it is then used by the thread, and the thread needs to execute8000 seconds Therefore, the SecondActivity object is referenced and cannot be released, causing a memory overflow.

To solve this kind of memory overflow, it is necessary to end the thread in time when the Activity exits (although it is not very good to end it...), or control the execution time of the thread well.

This way we have found the memory overflow in this program.

2.Directly use Android Studio's Monitor Memory to find memory overflow

We still use the above program, I'll just say it simply.

Firstly, run the program on the phone, open the AS Monitor interface and view the Memory image

Click the small truck icon (in the figure1(location icon) can trigger a GC


Click the image in the figure2The location icon can view the hprof file

On the left is the object in memory, find the Activity inside to see if it exists, the Activity we hope to have been recycled. If the Activity we expect to have been recycled appears, clicking it will display its total count on the right. Clicking one on the right can display its tree relationship diagram of GC Roots, and by looking at the diagram, you can find the location of the memory leak (similar to the first method).

This completes the search for memory leaks.

The causes of memory leaks in Android are roughly divided into the following categories:

1.Memory leak caused by static variable

Because the lifecycle of static variables starts when the class is loaded and ends when the class is unloaded, that is to say, static variables are only released when the program process dies. If an Activity is referenced in the static variable, then this Activity, due to the reference, will have the same lifecycle as the static variable and will be unable to be released, causing a memory leak.

Solution:

When the Activity is referenced by a static variable, use getApplicationContext because the lifecycle of Application starts from the beginning of the program to the end, which is the same as that of static variables.

2.Memory leak caused by thread

In a situation similar to the above example, the thread execution time is very long, and it will still execute even if the Activity exits in time, because the thread or Runnable is an inner class of Activity, thus holding the instance of Activity (because the creation of an inner class must rely on the outer class), causing the Activity to be unable to be released.

AsyncTask has a thread pool, and the problem is more serious.

Solution:

1Reasonably arrange the execution time of threads, control the thread to end before the Activity ends.

2Change the inner class to a static inner class and use WeakReference to save the Activity instance because WeakReference will be collected as soon as GC finds it, so it can be recycled as soon as possible

3BitMap occupies too much memory

Bitmap parsing requires memory, but memory only provides8Allocate memory to BitMap, if there are too many images and bitmaps are not recycled in time, it will cause memory overflow.

Solution:

Recycle and compress images in time before loading images

4Memory leaks caused by not closing resources in time

For example, some Cursors are not closed in time, which may save references to Activity, causing memory leaks

Solution:

Close it in time in the onDestory method

5Memory leaks caused by the use of .Handler

In the use of Handler, the handler sends the message object to the MessageQueue, and then Looper polls the MessageQueue, and then retrieves the Message to execute. However, if a Message is not executed for a long time, then due to the reference of Handler in the Message, and Handler is generally an inner class object, Message references Handler, and Handler references Activity, which makes Activity unable to be recycled.

Solution:

Still using static inner class+The weak reference method can solve it

There are still some issues with memory leaks, such as unremoved collection objects, unregistered objects, and code pressure, which may also cause memory leaks. However, the solutions mentioned above are generally effective.

You may also like