MemoryLeakAnalysis-Handler导致的内存泄露

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package info.enjoycode.handlerleak;

import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d(TAG, "handleMessage() called with: " + "msg = [" + msg + "]");
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "onCreate() called with: " + "savedInstanceState = [" + savedInstanceState + "]");
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
Log.d(TAG, "run() called with: " + "");
}
},60000);
}

}

模拟内存泄露,现象初步分析

  • 摇晃5次以后控制台输出

  • 可以看出,MainActivity,Handler,Runnable对象泄露了6次

  • 经过60秒后,MainActivity,Handler还是6次,Runnable对象已经回收了。

换个姿势看问题

  • Actions-Histogram-选中需要分析的包
  • 通过下图可以看到Handler和Runnable对象60秒内都存在的
  • 下面的图是60秒以后

出现原因

排队中的Message对象对Handler的持有导致泄漏

  • 当Android程序第一次创建的时候,在主线程同时会创建一个Looper对象。Looper实现了一个简单的消息队列,一个接着一个处理Message对象。程序框架所有主要的事件(例如:屏幕上的点击时间,Activity生命周期的方法等等)都包含在Message对象中,然后添加到Looper的消息队列中,一个一个处理。主线程的Looper存在整个应用程序的生命周期内。
  • 当一个Handler对象在主线程中创建的时候,它会关联到Looper的 message queue 。Message添加到消息队列中的时候Message会持有当前Handler引用,当Looper处理到当前消息的时候,会调用Handler#handleMessage(Message).
  • 在java中,no-static的内部类会 隐式的 持有当前类的一个引用。static的类则没有。

handler 引用 Activity 阻止了GC对Acivity的回收

  • 在Java中,非静态(匿名)内部类会默认隐性引用外部类对象。而静态内部类不会引用外部类对象。
  • 如果外部类是Activity,则会引起Activity泄露 。

    当Activity finish后,延时消息会继续存在主线程消息队列中1分钟,然后处理消息。而该消息引用了Activity的Handler对象,然后这个Handler又引用了这个Activity。这些引用对象会保持到该消息被处理完,这样就导致该Activity对象无法被回收,从而导致了上面说的 Activity泄露。

  • Handler的实现类采用静态内部类的方式,避免对外部类的强引用,在其内部声明一个WeakReference引用到外部类的实例。

ref

Android 开发进阶之『清除应用中的内存泄漏』

高效地分析Android内存–MAT工具解析

文章目录
  1. 1. 代码示例
  2. 2. 模拟内存泄露,现象初步分析
  3. 3. 换个姿势看问题
  4. 4. 出现原因
    1. 4.1. 排队中的Message对象对Handler的持有导致泄漏
    2. 4.2. handler 引用 Activity 阻止了GC对Acivity的回收
  5. 5. ref