A memory leak is when the memory of an object that is no longer being used cannot be reclaimed by the GC, and frequent GC can cause a lag. Android has a limited amount of memory allocated to each application, so if the application has a lot of memory leaks, it can easily cause OOM

Java memory allocation policy

There are three memory allocation strategies for Java program runtime, namely, static allocation, stack allocation, and heap allocation. Correspondingly, the memory space used by the three storage strategies is mainly static storage area (also known as method area), stack area and heap area.

• Static storage area (method area) : stores static data, global static data, and constants. This memory is allocated when the program is compiled and remains for the entire duration of the program.

• Stack area: When a method is executed, local variables in the method body are created on the stack, and the memory held by these local variables is automatically freed at the end of the method execution. Because stack allocation operations are built into the processor’s instruction set, it is efficient, but the allocated memory capacity is limited.

• Heap area: Also known as dynamic memory allocation, usually refers to the memory that is directly new while the program is running. This content is collected by the Java garbage collector when not in use.

Memory leak prevention

1. Avoid using static static variables

When static modifies a member variable, the variable belongs to the class, not an instance of the class. If you do that, that means the lifetime of the member variable will be stretched out to match the lifetime of the entire app process. A static variable has a long lifetime and can leak memory if it is used to refer to an object that consumes too much resources

When using static variables, note the following: First, you should avoid using static member variables to reference resource-consuming objects. Second, when a static variable refers to an object, it is necessary to set the reference to null when the object is no longer in use.

2. Holding singletons or long-life classes can cause memory leaks

The singleton pattern allows only one instance object to exist in the application, and the lifetime of the instance object is the same as the lifetime of the application. If a reference to another object exists in the singleton, the referenced object cannot be reclaimed in time. (Using weak references)

If the context is an Activity, it will leak memory. If the context is an Application, it will not leak memory because the Application has a long life cycle.

public class AppManager {
    private static AppManager instance;
    private Context context;
    private AppManager(Context context) {
        this.context = context;
    }
    public static AppManager getInstance(Context context) {
        if(instance ! =null) {
            instance = new AppManager(context);
        }
        returninstance; }}Copy the code

Memory leaks caused by a long life cycle, such as an Application, because its life cycle is as long as the entire Application life cycle:

public class MyApplication extends Application {


private Activity currenActivity;

public void setCurrenActivity(Activity currenActivity){
    this.currenActivity = currenActivity; }}Copy the code
public class TestActivity4 extends Activity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        MyApplication application = (MyApplication) getApplication();
        application.setCurrenActivity(this); }}Copy the code

3. Memory leakage caused by Handler

public class TestActivity2 extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run(a) {
                Log.i("TestActivity2"."Turned on"); }},1000000); }}Copy the code

If the Activity is started and then immediately shut down, a memory leak will occur. We know that Handler, Message, and MessageQueue are related to each other. The Handler interacts with the main thread by sending a Message. If the Message sent by Handler has not been processed yet, The Message and the Handler object that sent it will be held by MessageQueue forever, which may result in the Message not being recycled. In this case, Runable is a memory leak message, and because the anonymous inner class holds references to the outer class, it causes the Activity to leak. In this case, however, the memory leak is not very harmful because the message is only executed one second late. Handler can be used in the following ways:

public class TestActivity2 extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        new NewHandler(this).sendEmptyMessageDelayed(0.1000000);
    
    }

    private static class NewHandler extends Handler{
    
        private WeakReference<Activity> weakActivity;
    
        NewHandler(Activity activity){
            weakActivity = new WeakReference<>(activity);
        }
    
        @Override
        public void handleMessage(Message msg) {
            Activity activity = weakActivity.get();
            if(activity! =null){
                Log.i("TestActivity2",activity.getClass().getSimpleName()+"Start"); }}}}Copy the code

Defining handlers as static classes does not hold references to activities directly, whereas activities are held as weak references. When an object has only weak references, it is just as good as no references, and it will be immediately reclaimed when GC starts.

So: The Handler class needs to be declared static, otherwise it will leak. When Message enters the Message queue, it holds a reference to the target Handler. If the Handler is an inner class, the inner class also holds a reference to the external class. To avoid leakage of the external class, the Handler should be declared as a statically nested class, holding weak references to the external class.

4. Memory leaks caused by unclosed resources

Resources that use BraodcastReceiver, ContentObserver, File, Cursor, Stream, Bitmap, etc., should be shut down or logged out when the Activity is destroyed; otherwise, these resources will not be reclaimed, causing memory leaks.

5. Static instances of non-static inner classes are prone to memory leaks, such as static views or singleton patterns

Blog.csdn.net/linyukun642…

A static instance is created in a non-static inner class, causing the life cycle of the instance and applying the ClassLoader level. Because the static instance implicitly holds references to its external class, the external class cannot be released properly, causing leakage problems.

6. Object registration and unregistration are not paired to cause memory leaks; Such as registering broadcast receivers, registering observers (typically such as database listeners), and so on

7. Create and close leaks that do not occur in pairs; For example, Cursor resources must be closed manually, WebViews must be destroyed manually, and streams and other objects must be closed manually

8. Reuse resources as much as possible; For example, the system itself has many strings, colors, pictures, animations, styles and simple layout resources for us to use directly, we also try to reuse style resources to save memory

9. Implement onLowMemory() and onTrimMemory() methods as far as possible for applications with caches

10. Do not load bitmaps that are too large; For example, bitmapFactory. Options is used to set the sampling ratio and reuse of images

11. Cache design for batch loading operations, such as list image display, convertView cache of Adapter, etc

Memory Leak Management

1. Tools: Android Memory Profiler

When you first open the Memory Profiler, you see a detailed timeline of the amount of Memory used by your application and access various tools for enforcing garbage collection, capturing heap dumps, and recording Memory allocation.















































2. How to calculate memory

The number you see at the top of the Memory Profiler (Figure 2) depends on how many private Memory pages your application commits according to the Android system mechanism. This count does not include pages shared with the system or other applications.

The categories in the memory count are as follows: • Java: Object memory allocated from Java or Kotlin code. • Native: Object memory allocated from C or C++ code. Even if you don’t use C++ in your application, you might see some of the native memory used here, because the Android framework uses native memory to represent you for tasks such as image resources and other graphics, even if you write code in Java or Kotlin. • Graphics: Graphics buffer queues display memory used by pixels (including GL surfaces, GL textures, and so on) to the screen. (Please note that this is CPU-shared memory, not GPU-dedicated memory.) • Stack: The memory used by the native Stack and Java Stack in your application. This usually depends on how many threads your application runs. • Code: The memory your application uses to process Code and resources such as dex bytecodes, optimized or compiled dex codes,.so libraries, and fonts. • Other: Memory that your application uses that the system is not sure how to classify. • Allocated: Number of Java/Kotlin objects Allocated to your app. It doesn’t count toward objects allocated in C or C++. When connecting to devices running Android 7.1 and below, this allocation only starts counting when the Memory Profiler connects to your running application. Therefore, any objects allocated before you start the analysis will not be counted. However, Android 8.0 comes with a built-in device analysis tool that keeps track of all allocations, so on Android 8.0 and later, this number always represents the total number of Java objects to be processed in your application. The new Memory Profiler records your Memory differently than the Memory counts in the previous Android Monitor tools, so your Memory usage may now look higher. The Memory Profiler monitors more categories, which increases the total Memory usage, but if you only care about The Java heap Memory, the number of “Java” entries should be similar to the number in the previous tool. However, the Java number may not be exactly the same as the number you see in Android Monitor, because the application’s Java heap is started from Zygote, and the new number counts toward all the physical memory pages assigned to it. Therefore, it can accurately reflect how much physical memory your application is actually using. Note: Currently, the Memory Profiler also displays the native Memory usage of some false positives in the application, which is actually used by the analysis tool. For approximately 100,000 objects, this increases the reported memory usage by up to 10MB. In future versions of these tools, these numbers will be filtered out of your data.

3. Capture heap dumps

Heap dumps show which objects in your application were using memory at the time you captured the heap dump. Especially after a long user session, the heap dump helps identify memory leaks by showing objects that are still in memory that you thought should no longer be there. After the heap dump is captured, you can view the following information: • Which types of objects have been allocated by your application and how many for each type. • How much memory each object is using. • Where in the code each object is still referenced. • The call stack to which objects are assigned. (Currently, if you capture heap dumps while recording allocations, the call stack is only available for heap dumps in Android 7.1 and later.)

To capture a heap Dump, click Dump Java Heap in the Memory Profiler toolbar


















In the class list, you can view the following information: • Heap Count: number of instances in the Heap. • Shallow Size: The total Size, in bytes, of all instances in the heap. • Retained Size: The total Size (in bytes) of memory Retained for all instances of this type. At the top of the class list, you can use the left drop-down list to switch between the following heap dumps: • Default heap: when the system does not specify a heap. • App heap: The main heap to which your application allocates memory. • Image heap: System boot Image, containing pre-loaded classes during boot. The allocation here is guaranteed never to move or disappear. • Zygote Heap: copy-on-write heap where application processes are derived from the Android system. By default, the list of objects in this heap is arranged by class name. You can use other drop-down lists to switch between the following arrangements: • Arrange by class: Groups all assignments based on class names. • Arrange by Package: Groups all assignments based on software package names. • Arrange by callstack: groups all assignments into their corresponding callstack. This option is only available if a heap dump is captured during record allocation. Even so, objects in the heap are likely allocated before you start recording, so these allocations are shown first and listed only by class name. By default, this list is ordered by Retained Size column. You can click on any column header to change how the list is sorted. In Instance View, each Instance contains the following information: • Depth: The shortest number of hops from any GC root to the selected Instance. • Shallow Size: The Size of this instance. • Retained Size: The Size of memory controlled by this instance (based on the Dominator tree).