Android performance optimization memory leak, you want here~

Posted by happypete on Thu, 10 Feb 2022 10:26:17 +0100

preface

In Android, memory leakage is very common; The consequences of memory leakage will make the application Crash
This article comprehensively introduces the essence, causes and solutions of memory leakage, and finally provides some common memory leakage analysis tools. I hope you will like them.

catalogue

1.png

1. Introduction

ML (Memory Leak)
It refers to the phenomenon that the memory can not be released & returned to the program after the program has applied for the memory when it is no longer needed to be used

2. Impact on Applications

It is easy for the application to overflow memory, that is, OOM
Introduction to memory overflow:

1.png

3. Essential causes of memory leakage

Specific description

1.jpg

  • Particular attention
    From the perspective of mechanism, due to the garbage collection mechanism (GC) in Java, there should be no memory leakage; The reason for memory leakage is only external human reason = holding object references unconsciously, making the life cycle of the holder of references > the life cycle of the referenced

4. Reserve knowledge: Android memory management mechanism

4.1 introduction

1.png

Next, the memory allocation & recycling of recycling processes, objects and variables will be explained in detail

4.2 memory strategy for process

a. Memory allocation strategy

The memory allocation of all processes is centrally managed by the ActivityManagerService

b. Memory reclamation strategy

Step 1: the Application Framework determines the type of recycling process
Processes in Android are hosted; When the process space is tight, the process will be automatically recycled in the order of low - > > high process priority
Android divides the process into five priority levels, as follows:

1.png

  • Step 2: the Linux kernel really recycles specific processes
    ActivityManagerService scores all processes (the scores are stored in the variable adj)
    Update score to Linux kernel
    Real memory recycling is done by the Linux kernel

Here is only a summary of the process. The process is complex. Interested readers can study the system source code activitymanagerservice java

4.2 memory strategy for objects and variables

  • Android's memory strategy for objects and variables is the same as that of Java
  • Memory management = memory allocation of objects / variables + memory release

Next, the memory allocation & memory release strategy will be explained in detail

a. Memory allocation strategy
  • The memory allocation of objects / variables is automatically undertaken by the program
  • There are three types: static allocation, stack allocation and & heap allocation, which are respectively oriented to static variables and local variables & object instances
    The details are as follows
  

![](https://upload-images.jianshu.io/upload_images/5258053-9c0b664ad81e4345.png)

1.png

  

Note: use an example to explain memory allocation

public class Sample {    
    int s1 = 0;
    Sample mSample1 = new Sample();   
    
    // The local variables s2 and mSample2 in the method are stored in the stack memory
    // The object instance pointed to by the variable mSample2 is stored in heap memory
      // The member variables s1 and mSample1 of this instance are also stored in the stack
    public void method() {        
        int s2 = 0;
        Sample mSample2 = new Sample();
    }
}
    // The object instance pointed to by the variable mSample3 is stored in heap memory
    // The member variables s1 and mSample1 of this instance are also stored in heap memory
    Sample mSample3 = new Sample();

b. Memory release strategy
  • The memory release of objects / variables is the responsibility of the Java garbage collector (GC) / frame stack
  • Here we mainly explain the memory release strategy of object allocation (i.e. heap allocation) = Java garbage collector (GC)

Because static allocation does not need to be released, and stack allocation is only automatically out and in the stack through the frame stack, it is relatively simple, so it is not described in detail

  • Memory release of Java garbage collector (GC) = garbage collection algorithm, mainly including:
  

![](https://upload-images.jianshu.io/upload_images/5258053-2695060b1627b587.png)

1.png
  • The details are as follows
  

![](https://upload-images.jianshu.io/upload_images/5258053-c53cce28814ab12e.png)

1.png

5. Common causes and solutions of memory leakage

Common causes of memory leakage include:

  1. Collection class
  2. Static keyword modified member variable
  3. Non static inner class / anonymous class
  4. Resource object is not closed after use
    Next, I will describe in detail the causes of each memory leak

5.1 set class

  • Cause of memory leak
    After adding elements to the collection class, the collection element object is still referenced, resulting in the collection element object can not be recycled, resulting in memory leakage
    Example demonstration:
// Apply for Object objects through cycle & put the applied objects into the collection List one by one
List<Object> objectList = new ArrayList<>();        
       for (int i = 0; i < 10; i++) {
            Object o = new Object();
            objectList.add(o);
            o = null;
        }
// Although the collection element reference itself is released: o=null)
// However, the collection List still refers to this object, so the garbage collector GC still cannot recycle this object
  • Solution
    After adding a collection element object to a collection class, it must be deleted from the collection after use

Since there are many elements in a collection, the simplest method = empty the collection object & set to null

 // Release objectList
        objectList.clear();
        objectList=null;

5.2 member variables modified by static keyword

  • Reserve knowledge
    Life cycle of member variable modified by Static keyword = life cycle of application
  • Cause of leakage
    If the member variable modified by Static keyword refers to an instance that consumes too much resources (such as Context), it is easy to see that the life cycle of the member variable > the life cycle of the reference instance. When the reference instance needs to be destroyed at the end of the life cycle, it will not be recycled due to the holding of Static variables, resulting in memory leakage
    Example explanation:
public class ClassName {
 // Define 1 static variable
 private static Context mContext;
 //...
// It refers to the context of Activity
 mContext = context; 

// When the Activity needs to be destroyed, because mContext = static & life cycle = application life cycle, the Activity cannot be recycled, resulting in memory leakage

}
  • Solution
  1. Try to avoid instances where Static member variables reference resources that consume too much (such as Context)

If the Context needs to be referenced, try to use the Context of Applicaiton

  1. Use a weak reference instead of a strong reference to hold instances

Note: there is a very typical example of static member variable = singleton mode

  • Reserve knowledge
    Due to its static characteristics, the length of its life cycle = the life cycle of the application
  • Cause of leakage
    If an object no longer needs to be used and the singleton object still holds the reference of the object, the object will not be recycled normally, resulting in memory leakage

Example demonstration:

// When creating a singleton, you need to pass in a Context
// If the Context of an Activity is passed in, the singleton will hold the reference of the Activity
// Since the singleton always holds the reference of the Activity (until the end of the entire application life cycle), the memory of the Activity will not be recycled even if the Activity exits
// Especially for some huge activities, it is very easy to cause OOM here

public class SingleInstanceClass {    
    private static SingleInstanceClass instance;    
    private Context mContext;    
    private SingleInstanceClass(Context context) {        
        this.mContext = context; // The context of Activity is passed
    }  
  
    public SingleInstanceClass getInstance(Context context) {        
        if (instance == null) {
            instance = new SingleInstanceClass(context);
        }        
        return instance;
    }
}
  • Solution
    The life cycle of the object referenced by the singleton pattern = the life cycle of the application

As in the above example, the Context of Application should be passed, because the life cycle of Application = the life cycle of the whole Application

public class SingleInstanceClass {    
    private static SingleInstanceClass instance;    
    private Context mContext;    
    private SingleInstanceClass(Context context) {        
        this.mContext = context.getApplicationContext(); // The context of Application is passed
    }    

    public SingleInstanceClass getInstance(Context context) {        
        if (instance == null) {
            instance = new SingleInstanceClass(context);
        }        
        return instance;
    }
}

5.3 non static inner class / anonymous class

  • Reserve knowledge
    Non static internal class / anonymous class holds the reference of external class by default; Static inner classes do not
  • Common situations
    Three types: instance of non static internal class = static, multithreading, and message passing mechanism (Handler)

5.3.1 instance of non static inner class = static

  • Cause of leakage
    If the instance created by the non static internal class = static (its life cycle = application life cycle), the external class cannot be released because the non static internal class holds the reference of the external class by default, resulting in memory leakage

That is, static objects that hold non static internal classes in external classes
Example demonstration:

// Background:
   a. Frequently started Activity In order to avoid duplicate creation of the same data resources, the Activity Create a singleton of a non static inner class internally
   b. Every start Activity The data of this singleton will be used when

public class TestActivity extends AppCompatActivity {  
    
    // A reference to an instance of a non static inner class
    // Note: set to static  
    public static InnerClass innerClass = null; 
   
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {        
        super.onCreate(savedInstanceState);   

        // Ensure that there is only 1 instance of non static inner class
        if (innerClass == null)
            innerClass = new InnerClass();
    }

    // Definition of non static inner class    
    private class InnerClass {        
        //...
    }
}

// Causes of memory leakage:
    // a. When TestActivity is destroyed, because the life cycle of the reference (innerClass) of the non static internal class singleton = the life cycle of the application App and the reference of the external class TestActivity
    // b. Therefore, TestActivity cannot be recycled by GC, resulting in memory leakage
  • Solution
  1. Set the non static inner class as: static inner class (static inner class does not hold the reference of external class by default)
  2. The inner class is extracted and encapsulated into a singleton
  3. Try to avoid instances created by non static inner classes = static

If you need to use Context, it is recommended to use the Context of Application

5.3.2 multithreading: AsyncTask, implementing Runnable interface, inheriting Thread class

  • Reserve knowledge
    Usage method of multithreading = non static internal class / anonymous class; That is, the thread class belongs to non static internal class / anonymous class
  • Cause of leakage
    When the worker thread is processing a task & the external class needs to be destroyed, because the worker thread instance holds the external class reference, the external class cannot be recycled by the garbage collector (GC), resulting in memory leakage

Multithreading mainly uses AsyncTask, implements Runnable interface and inherits Thread class
The principle of memory leakage of the first three is the same. Here we mainly take inheriting Thread class as an example

Example demonstration

/** 
     * Method 1: create a new Thread subclass (internal class)
     */  
        public class MainActivity extends AppCompatActivity {

        public static final String TAG = "carson: ";
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);

            // Multithreading is realized by creating internal classes
            new MyThread().start();

        }
        // Custom Thread subclass
        private class MyThread extends Thread{
            @Override
            public void run() {
                try {
                    Thread.sleep(5000);
                    Log.d(TAG, "Multithreaded execution");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

   /** 
     * Method 2: anonymous Thread inner class
     */ 
     public class MainActivity extends AppCompatActivity {

    public static final String TAG = "carson: ";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Multithreading through anonymous inner classes
        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(5000);
                    Log.d(TAG, "Multithreaded execution");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }.start();
    }
}


/** 
  * Analysis: cause of memory leak
  */ 
  // The worker Thread class belongs to a non static internal class / anonymous internal class. The reference of the external class is held by default at runtime
  // When the worker thread is running, if the external class MainActivity needs to be destroyed
  // Because the worker thread class instance holds the reference of the external class at this time, the external class cannot be recycled by the garbage collector (GC), resulting in memory leakage

  • Solution
    As can be seen from the above, there are two key conditions causing memory leakage:
  1. There is a reference relationship of "worker thread instance holds external class reference"
  2. The lifecycle of the worker thread instance > the lifecycle of the external class, that is, the worker thread is still running and the external class needs to be destroyed
    The idea of the solution = just make any of the above conditions untenable.
// There are two solutions: static inner class & force the thread to end when the outer class ends its life cycle
// The specific description is as follows

   /** 
     * Solution 1: static inner class
     * Principle: the static internal class does not hold the reference of external class by default, so that the reference relationship of "worker thread instance holds external class reference" no longer exists
     * Specific implementation: set the subclass of Thread to a static internal class
     */  
        public class MainActivity extends AppCompatActivity {

        public static final String TAG = "carson: ";
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);

            // Multithreading is realized by creating internal classes
            new MyThread().start();

        }
        // Analysis 1: Custom Thread subclass
        // Set to: static inner class
        private static class MyThread extends Thread{
            @Override
            public void run() {
                try {
                    Thread.sleep(5000);
                    Log.d(TAG, "Multithreaded execution");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

   /** 
     * Solution 2: force the thread to end when the external class ends its life cycle
     * Principle: synchronize the life cycle of the worker thread instance with the life cycle of the external class
     * Specific implementation: when the external class (take Activity as an example here) ends its life cycle (at this time, the system will call onDestroy()), the thread will be forced to end (call stop())
     */ 
     @Override
    protected void onDestroy() {
        super.onDestroy();
        Thread.stop();
        // When the life cycle of the external class Activity ends, the thread is forced to end
    }

5.3.3 message passing mechanism: Handler

Android memory leak: explain the causes and solutions of Handler memory leak in detail

5.4 resource object not closed after use

  • Cause of leakage
    For the use of resources (such as broadcast BraodcastReceiver, File stream File, database Cursor cursor, picture resource Bitmap, etc.), if these resources are not closed / logged off in time when the Activity is destroyed, these resources will not be recycled, resulting in memory leakage
  • Solution
    Close / log off resources in time when the Activity is destroyed
// For broadcast BraodcastReceiver: unregister
unregisterReceiver()

// For File stream: close stream
InputStream / OutputStream.close()

// For database cursor: close cursor after use
cursor.close()

// For image resources, Bitmap: Android allocates only 8M memory to images. If a Bitmap object occupies a large amount of memory, when it is no longer used, it should call recycle() to reclaim the memory occupied by the pixels of this object; Finally, it is assigned to null 
Bitmap.recycle();
Bitmap = null;

// For animation (attribute animation)
// Set the animation to play in an infinite loop after repeatCount = "infinite"
// Remember to stop the animation when the Activity exits

5.5 other uses

  • In addition to the above four common situations, there are some daily uses that will lead to memory leakage
  • It mainly includes Context, WebView and Adapter. The details are as follows
  

![](https://upload-images.jianshu.io/upload_images/5258053-052f56003d938b7c.png)

1.png

5.6 summary

Next, I will use a chart to summarize the causes and solutions of memory leakage in Android

1.png

6. Tools to assist in analyzing memory leaks

  • Even if we fully understand the causes of memory leakage, it is inevitable that memory leakage will occur
  • The following will briefly introduce several mainstream tools for analyzing memory leaks, which are
  1. MAT(Memory Analysis Tools)
  2. Heap Viewer
  3. Allocation Tracker
  4. Memory Monitor for Android Studio
  5. LeakCanary

6.1 MAT(Memory Analysis Tools)

  • Definition: an Eclipse Java Heap memory analysis tool - > > Download address
  • Function: view the current memory usage

By analyzing the memory snapshot HPROF analysis of Java processes, we can quickly calculate the size of objects in memory and see which objects cannot be recycled by the garbage collector & we can visually view the objects that may cause this result through the view

6.2 Heap Viewer

  • Definition: a Java Heap memory analysis tool
  • Function: view the current memory snapshot

You can view which types of data are in the total heap memory & the proportion of various types of data

6.3 Allocation Tracker

  • Introduction: a memory trace analysis tool
  • Function: track the memory allocation information and arrange it in order
  • Specific use: Allocation Tracker usage guide

6.4 Memory Monitor

  • Introduction: a graphical memory detection tool provided by Android Studio
  • Function: track the memory usage of the system / application. The core functions are as follows
  

![](https://upload-images.jianshu.io/upload_images/5258053-9449e4d758db0c95.png)

1.png

6.5 LeakCanary

7. Summary

This paper comprehensively introduces the essence, causes and solutions of memory leakage. I hope you will try to avoid memory leakage during development

For my inventory, please click My GitHub Free collection

Topics: Android