Android performance optimization (2) - memory leak

Android performance optimization (2) - memory leak

foreword

If you want to fully understand memory leaks, you must start from three aspects, what is memory leaks, what is the impact of memory leaks, and how to solve memory leaks.

  • What is a memory leak and its impact?

    Every object has a life cycle, and it will be recycled when the life cycle ends, but because other references that hold this object cannot be recycled, the memory cannot be released, and the accumulation in the heap memory over time will cause memory Leaks, because the memory allocated by the program is not enough to support the memory required for the program to run, which will eventually lead to program out-of-memory (OOM) and program crash, which is a fatal problem for program developers and users.

  • How to detect memory leaks?

    There are many memory detection tools, here I recommend two tools, one is MAT, and the other is Leak Canary three-party detection tool, both of which are very easy to use, there is no best tool, only the most suitable for you. I am using Leak Canary in this blog, and the specific usage method is linked to Github-LeakCanary usage method

  • How to fix memory leak ?

    Here I wrote a picture for everyone to enjoy, and I will explain some of the common memory leaks in detail later.

    write picture description here

  • The first common memory leak: memory leak caused by the creation of static instances of non-static inner classes

    Because the non-static inner class will hold the reference of the outer class, and a static instance is created by using the non-static inner class, because the life cycle of the static instance is as long as the life cycle of the application, the instance always holds the reference of the Activity , the resources of the Activity cannot be recycled normally, resulting in a memory leak. From the picture below, it can be seen that the memory leak is related to the demo in the current class, which is convenient to further check the cause.

    Solution: You can write the class as a static inner class or set the class to a singleton mode.

    public class StaticOOM1Activity extends AppCompatActivity {

        static Demo demo;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_static_oom1);
            RefWatcher refWatcher = App.getRefWatcher(this);
            refWatcher.watch(this);
            if (demo == null) {
                demo = new Demo();
            }
            finish();
        }

        class Demo {
        }
    }

Memory leak caused by creating static instance of non-static inner class


There is another situation as follows: because mContext is a static variable with a long life cycle, which is generally consistent with the process, then the code with mContext=this will hold the static variable a reference to the Activity, resulting in the Activity resource cannot be recycled, and the memory Leaks, which generally do not occur, are too obvious.

public class StaticOOM2Activity extends AppCompatActivity {

    private static Context mContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_static_oom1);
        RefWatcher refWatcher = App.getRefWatcher(this);
        refWatcher.watch(this);
        mContext = this;
        finish();
    }
}

write picture description here


  • The second common memory leak: memory leak caused by the singleton pattern

    Because the formal parameter of the singleton mode is a context at this time, if the this parameter is passed in the Activity, there will be a memory leak, because the life cycle of the singleton mode is the same as that of the Application, and when the Activity is destroyed, because the singleton mode The instance of Activity also holds a reference to the Activity, so the Activity cannot be recycled, resulting in a memory leak.

public class Person {

    private static Person person = null;
    Context mContext;
    private Person(Context context) {
        this.mContext = context;
    }

    public static Person getInstance(Context context) {
        if (person == null) {
            synchronized (Person.class) {
                if (person == null) {
                    person = new Person(context);
                }
            }
        }
        return person;
    }
}

Solution: Use context.getApplicationContext()/App.get() here to get the context of the entire program

public class Person {

    private static Person person = null;
    Context mContext;
    private Person(Context context) {
        this.mContext = context;
    }

    public static Person getInstance(Context context) {
        if (person == null) {
            synchronized (Person.class) {
                if (person == null) {
                    person = new Person(context.getApplicationContext());
//                    person = new Person(App.get());
                }
            }
        }
        return person;
    }
}

  • The third common memory leak: memory leak caused by property animation

    Because in the Activity, the animation will hold the reference of the View, and the View will hold the reference of the Activity. Even if the Activity is closed, the animation will continue to run, but we can't see it, which will cause memory leaks.

public class AnimationOOMActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_static_oom1);
        RefWatcher refWatcher = App.getRefWatcher(this);
        refWatcher.watch(this);

        Button button= (Button) findViewById(R.id.tv);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
        AnimatorSet set = new AnimatorSet();
        ObjectAnimator alpha = ObjectAnimator.ofFloat(button, "alpha", 1.0f, 0.5f);
        alpha.setRepeatCount(1000);
        alpha.setDuration(500);
        set.playTogether(alpha);
        set.start();
    }
}

Solution: Cancel the animation in the onDestory() method by animation.cancel().


  • The fourth common memory leak: memory leak caused by Handle.

    If we send a message with a delay of one minute, the message processing mechanism of Handler is believed to be familiar to everyone. The loop() method is used to loop through the messages in the message queue, because the loop() method is a blocking method. And the message is processed in the form of a queue, then within this minute MessageQueue will hold a reference to the message and handler, and the handler is a non-static inner class, it will hold a reference to the outer class, so when we When the Activity is destroyed, it will cause a memory leak.

public class HandleOOMActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_static_oom1);
        RefWatcher refWatcher = App.getRefWatcher(this);
        refWatcher.watch(this);

        mHandler.sendEmptyMessageDelayed(1, 60 * 1000);
        finish();
    }

    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == 1) {
                Toast.makeText(HandleOOMActivity.this, "收到了信息", Toast.LENGTH_SHORT).show();
            }
        }
    };

Solution: Declare Handler as a static inner class, so that it will not hold a reference to the outer class, it has nothing to do with Activity, and it will not cause memory leaks. Although there will be no memory leaks at this time, we know that There are still messages in the MessageQueue, and Looper is also waiting to process the messages, so we need to process the messages in the queue when the Activity ends. If you use non-static objects of external classes such as Context, you need to use Context with the same life cycle as the application.

public class HandleOOMActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_static_oom1);
        RefWatcher refWatcher = App.getRefWatcher(this);
        refWatcher.watch(this);

        MyHandler myHandler = new MyHandler(this);
        myHandler.sendEmptyMessageDelayed(1,60*1000);
        finish();
    }

    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == 1) {
                Toast.makeText(HandleOOMActivity.this, "收到了信息", Toast.LENGTH_SHORT).show();
            }
        }
    };

    private static final class MyHandler extends Handler {
        private WeakReference<HandleOOMActivity> mActivity;

        public MyHandler(HandleOOMActivity mainActivity) {
            //mActivity=mainActivity.getApplicationContext;
            mActivity = new WeakReference<>(mainActivity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            HandleOOMActivity mainActivity = mActivity.get();
            if (null != mActivity) {
                //相关处理
            }
        }
    }

    /**
     * 虽然我们结束了Activity的内存泄漏问题,但是经过Handler发送的延时消息还在MessageQueue中,
     * Looper也在等待处理消息,所以我们要在Activity销毁的时候处理掉队列中的消息。
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }
}

End: As written in the mind map above, there are actually many reasons for memory leaks, such as I/O stream problems, Bitmap resource release, registration and de-registration, and do not create objects repeatedly, etc. , as long as you summarize and pay more attention in the development, you will make progress. This blog is also an important aspect of performance optimization. Programmers, come on...

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325641830&siteId=291194637