Android performance optimization series - memory optimization

Memory is the lifeline of Android applications. Once there is a problem with the memory, the memory leak may cause the app to freeze, or the app may directly crash. Therefore, to keep an application robust, it is necessary to use and optimize the memory well. There are many good articles on the Internet about JAVA memory virtual machines, so I won’t go into details. Today we mainly summarize memory optimization.

As a developer, you need to pay more attention to your daily code. If there are any unreasonable things, you need to optimize them.

1. Memory leak:

A memory leak means that objects that are no longer used in the current application cycle are referenced by GC Roots, resulting in the inability to recycle, making the actual usable memory smaller. In layman's terms, it means that useless objects cannot be recycled.

2. Out of memory (also called OOM):

When the system applies for memory space, there is not always enough memory space for it to use. To put it simply, the system cannot allocate the memory space you need. For example, if you apply for 100M of memory space, but the system only has 90M left, the memory will overflow. (Overflow will cause the application to crash)

The essential causes of memory leaks:

Objects that should be recycled are not returned and continue to stay in the memory space, causing the memory to be occupied. In fact, the life cycle of the holder of the reference > the life cycle of the held reference. Excessive memory leaks will occupy the memory space and eventually lead to memory overflow.

3. Common situations of memory leaks

1. Non-static inner classes create static instances

Creating static instances of non-static inner classes will lead to memory leaks. Usually in projects, singleton data is usually created when the Activity is started to avoid repeatedly creating the same data. A static instance of the non-static inner class will be created inside the Activity.

Wrong example: non-static inner classes hold external references by default

public class NotStaticActivityextends AppCompatActivity {
    //非静态内部类实例引用,设置为静态
    private static InnerClass sInnerClass;
 
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        //保证非静态内部类实例只有一个
        if (sInnerClass == null) {
            sInnerClass = new InnerClass();
        }
    }
 
    //非静态内部类
    private class InnerClass {
        //……
    }
}

The life cycle of the non-static external class (InnerClass) = the life cycle of the application, and it holds a reference to the external class Activity. When the Activity is destroyed, it cannot be recycled by GC, resulting in memory leaks.

Optimized code 1: Set the non-static inner class to a static inner class (static inner classes do not hold references to external classes by default)

  //静态内部类
    private static class InnerClass2 {
        //……
    }

Optimized code 2: extract the external class and encapsulate it into a singleton

public class InnerClass  extends AppCompatActivity {
    private static final String TAG = InnerClass3.class.getSimpleName();
    private static InnerClass sInnerClass;
    //单例
    public InnerClass getInstance() {
        if (sInnerClass == null) {
            sInnerClass = new InnerClass();
        }
        return sInnerClass;
    }
}
2. The registered object has not been logged out or the resource object has not been closed.

If you register something like BraodcastReceiver and EventBus and do not log out when the page is destroyed, it will cause leakage problems, so you should log out in time when the Activity is destroyed.

3. Multi-threading causes memory leaks

We know that the thread class attribute is not a static (anonymous) inner class. The main uses of multi-threading are AsyncTask, implementing the Runnable interface, and inheriting the Thread class. When using the thread class, you need to pay attention to memory leaks.

1).AsyncTask

We often use AsyncTask to perform some asynchronous time-consuming operations.

We need to call the AsyncTask.cancel(true) method when the interface is destroyed.

@Override
    protected void onDestroy() {
       //强制退出AsyncTask
        mAsyncTask.cancel(true);
        super.onDestroy();
    }

2).Use of Thread and Handler

Thread: The thread class Thread is a non-static internal class. Its life cycle is longer than the life cycle of ThreadActivity. By default, the runtime holds a reference to the external class. If ThreadActivity needs to be destroyed, the thread class Thread holds a reference to ThreadActivity, causing ThreadActivity to fail. Recycling, causing memory leaks.

We often use it like this:


 
    //方式一:新建内部类
        new MyThread().start();
 
    //方式二:匿名Thread内部类
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
 
    //自定义Thread
    private class MyThread extends Thread {
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

Optimization method 1; Change private class MyThread extends Thread to a static inner class private static class MyThread extends Thread;

Optimization method 2: End the thread in the ondestroy method of the interface.

Handler: It is also an internal class. When we use it, we will also remind it that if it is non-static, it will cause a memory leak.

Solution 1:

  If  Handler you don't need external  Activity data, just use static inner classes or external classes directly, and avoid using member inner classes. If necessary, you can also use the external class + weak reference method.

private static class MyHandler extends Handler {
     private WeakReference<MainActivity> ref;

     public MyHandler(Activity activity) {
         this.ref = new WeakReference<>(activity);
     }

     @Override
     public void handleMessage(@NonNull Message msg) {
         // TODO:ref.get()...
         
         super.handleMessage(msg);
     }
}

Tips: It is not possible to use member inner classes + weak references. The inner class holds the reference to the outer class by default.

Solution 2: When the external class ends its life cycle, clear the message queue in the Handler;

   @Override
    protected void onDestroy() {
        //当外部类结束生命周期时,清空Handler内消息队列;
        mHandler.removeCallbacksAndMessages(null);
        super.onDestroy();
    }
4. Member variables modified by the static keyword

We know that the life cycle of member variables modified by static is equal to the life cycle of the app application.

So, can some unnecessary ones be removed and changed from static to non-static.

If a static object refers to an activity, it is enough to replace it with the context of the App.

5. Whether the referenced object is recycled or left empty

(1) After using animation-related resources, remember to recycle them in time;

(2) IO stream related classes: When using IO stream, File file class or Sqlite, Cursor and other resources, they must be closed in time. These resources are generally buffered during read and write operations. If not closed in time, these buffer objects will They have been occupied and cannot be released, resulting in memory leaks. Therefore, when they are not needed, they should be closed in time and buffered to release resources to avoid memory leaks.

  1. stream.close();

  2. cursor.close();

  3. bitmap.recycle();bitmap = null;

  4. arrayList.clear();arrayList = null;

6. Memory jitter

When a large number of objects are created and recycled, memory jitter will occur. For example, it is common to create objects in the onDraw method. The objects should be placed in the class or initialization method.

Guess you like

Origin blog.csdn.net/LoveFHM/article/details/135465061