During the android development process, we may encounter a frustrating OOM exception, which we are both familiar with and hate, because the cause of OOM is a variety of circumstances, such as the loading image is too large, a class that is no longer used is not timely collected by GC, etc. In this article, we’ll take a look at one of the OOM scenarios: the evil memory leak. For such a name, we are not unfamiliar, and even “side by side” with it many times, but it is a pig teammate, will only continue to send tower…… .

This paper is divided into three parts:

Handler Memory leakage example description and principle description 2. Problem verification (skip this problem if it is too complex) 3.Handler Memory leakage solution eg.Handler Memory leakage example description and principle description

1.Handler Memory leakage examples and working principles

Handler, we’re pretty familiar with it, and we use it a lot, but it’s because we’re so familiar that we occasionally get stabbed in the back and bleed… Remember when we used to confidently use beautiful and concise code like this?

The handler code

In case you are scared, to tell you the truth, this code has caused memory leak!! Don’t believe me? Let’s use the Android Lint tool to check the code for this class:

Android lint

Let’s face it, why is that? In Java, both non-static inner classes and anonymous inner classes implicitly hold an external reference to the current class. Since a Handler is a non-static inner class, it holds an implicit reference to the current Activity. If the Handler is not released, the external reference it holds, the Activity, cannot be released. When an object is a don’t need to use again, should be recycled, and another is using object holds its reference which can lead to it can’t be recycled, the cause should be recycled object can’t be recycled and stay in the heap memory, this creates a memory leak (it is for this reason the above example). Eventually, the OOM… Let’s use mHandler to send a delayed message:

enter description here

Analysis: When we execute the HandlerActivity interface, the delayed message will remain in the main thread message queue for 5 minutes before being processed. This message contains a reference to Handler, which is an instance of an anonymous inner class. It holds a reference to an external HandlerActivity, which causes the HandlerActivity to fail to be recycled, which causes many resources held by the HandlerActivity to fail to be recycled, thus causing the legendary memory leak problem!

2. Problem verification (if you feel cumbersome, please skip directly)

To further verify the memory leak problem, we create an int array in the class that allocates 2m of memory, then we use DDMS to look at the heap memory, and then GC to see if the memory changes:

enter description here

Allocated memory: 8.5m, free memory :4M, we frequently click GC button, the memory has not changed significantly, now we click the phone to go back to the key, push the app and re-enter, also check the head memory:

enter description here

We found the head of memory: 20.5 M, Allocated: 16.5 M, Free: 4 M, heap memory and has Allocated memory almost doubled, we continue to frequent click GC, see can be recycled? Now let’s go back to the phone, launch the app, and re-enter. Again check the head memory:

enter description here

We found the head of memory: 28.5 M, Allocated: 24.5 M, Free: 4 M, heap memory and has Allocated memory and increased, and no matter how we click the GC recycling memory, memory, have no obvious change, and every launch the page, double memory! When a class is created but not destroyed, there is a memory leak. To further verify this question, we use the MAT tool to look at a set of Histogram data and Dominator Tree data, starting with Histogram data:

The Histogram of the data

Dominator Tree data:

Dominator tree data

The existence of three identical HandlerActivity and inner classes at the same time is sufficient to indicate that the HandlerActvity was created and not destroyed, which means that the memory leak caused by the Handler does exist.

3.Handler Indicates the solution to memory leakage

The idea is to use static inner classes that inherit from Handler (or can be stored as a separate class file). Because static inner classes do not hold references to external classes, there is no memory leak of external class instances. When you need to invoke an external Activity in a static inner class, we can use weak references to handle this. Also set Runnable to a static member property. Modified code that does not leak memory is as follows:

Method: Declare static anonymous inner class + weak reference

public class HandlerActivity extends Activity {

    // Create a 2M int array

    int[] datas=new int[1024*1024*2];

// Handler mHandler = new Handler(){

// @Override

// public void handleMessage(Message msg) {

// super.handleMessage(msg);

/ /}

/ /};

    / * *

* Create static inner classes

* /


    private static class MyHandler extends Handler{

        // Holds a weak reference to HandlerActivity, which will be recycled during GC collection.

        private final WeakReference<HandlerActivity> mActivty;

        public MyHandler(HandlerActivity activity){

            mActivty =new WeakReference<HandlerActivity>(activity);

        }

        @Override

        public void handleMessage(Message msg) {

            HandlerActivity activity=mActivty.get();

            super.handleMessage(msg);

            if(activity! =null) {

                // Execute business logic

            }

        }

    }

    private static final Runnable myRunnable = new Runnable() {

        @Override

        public void run(a) {

            // Execute our business logic

        }

    };

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_handler_leak);

        MyHandler myHandler=new MyHandler(this);

        // Fixed memory leak, delay 5 minutes after delivery

        myHandler.postDelayed(myRunnable, 1000 * 60 * 5);

    }

}

Copy the code