内存泄漏

Handler内存泄漏分析

public class MainActivity extends AppCompatActivity {    private final int MESSAGE_WHAT = 10000;    Handler handler = new Handler() {        @Override        public void handleMessage(@NonNull Message msg) {            super.handleMessage(msg);            handler.sendEmptyMessageDelayed(MESSAGE_WHAT, 3 * 1000);        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        handler.sendEmptyMessageDelayed(MESSAGE_WHAT, 5 * 1000);    }}

在启动时,用Handler发空消息,然后在处理消息里面又发空消息。我们启动MainActivity 之后,1s之内点返回,然后在Profile里面点多次回收,按理说MainActivity 应该被GC回收掉,没有MainActivity 的实例。 但是实际上,如下图所示,还有一个MainActivity 实例。

可以看到,我截取的部分是点了很多次GC的,很多个垃圾桶的标志。但是这个MainActivity仍然有一个实例,说明MainActivity已经泄露了。

泄漏分析


这个按钮(dump the heap)能自动截取一段内存信息,生成一个文件。

生成完点这里保存到磁盘中,后缀名是hprof

然后使用sdk的工具hprof-conv,转一下,转成mat工具可以使用的。

.\hprof-conv.exe .\test1.hprof .\test2.hprof


prof-conv xxxxx.hprof yyyyy.hprof,其中xxxxx.hprof为原始文件,yyyyy.hprof为转换过后的文件。

使用mat工具打开test2.hprof文件。Mat:下载地址
打开之后,点一下下图位置

在ClassName的地方输入MainActivity,然后回车,找到MainActivity的所有实例。

然后排除软、若、虚引用,也就是只留强引用。

然后mat会打开新的标签页,用来展示这个MainActivity的引用信息

从图中我们可以看到MainActivity被MainActivity$1引用,然后一层一层到上面。哦了,分析到这里,我们也能从证据上看出来MainActivity因为MainActivity$1的强引用导致内存泄漏了。

理论分析

首先非静态内部类持有外部类引用,其次Handler发送Message,会把本身交给Message,Message放到Looper的Queue中。倒过来推就是,Looper中引用Queue,Queue引用Message,Message引用Handler,Handler引用MainActivity。所以MainActivity内存泄漏了。

解决办法

static修饰

 private static final int MESSAGE_WHAT = 10000;    static Handler handler = new Handler() {        @Override        public void handleMessage(@NonNull Message msg) {            super.handleMessage(msg);            handler.sendEmptyMessageDelayed(MESSAGE_WHAT, 3 * 1000);        }    };


经过垃圾回收,MainActivity已经被回收掉了,没有MainActivity的实例了。

缺点:static声明过多, 会造成类加载速度变慢,因为static是跟着类一起加载的。

软引用(这个在非静态内部类无法控制)

在Handler这个例子中没法测试,但是其它的可以试试的。

缺点:何时被回收不确定,如果在回收之后还需要用,就比较麻烦了。

销毁Activity时移除消息

public class MainActivity extends AppCompatActivity {    private final int MESSAGE_WHAT = 10000;    Handler handler = new Handler() {        @Override        public void handleMessage(@NonNull Message msg) {            super.handleMessage(msg);            handler.sendEmptyMessageDelayed(MESSAGE_WHAT, 3 * 1000);        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        handler.sendEmptyMessageDelayed(MESSAGE_WHAT, 5 * 1000);    }    @Override    protected void onDestroy() {        super.onDestroy();        handler.removeMessages(MESSAGE_WHAT);    }


在onDestroy回调方法中,移除Message,这样引用链就会在Queue–Message这里断开,下面的对象就可以回收了。从截图中可以看到不论MainActivity还是Handler都被正确回收了。

缺点:如果Handler写的比较多,这样的做法导致代码量比较多。

总结

内存泄漏的原因就是 短生命周期的实例,被长生命周期的实例引用,导致短生命周期的实例无法被回收。具体如何解决内存泄漏问题,不可一概而论,具体问题,具体分析,如果上面的例子中,Handler处理的事情很简单,那么即使MainActivity销毁,只要Handler的事情做完了,还是可以正常回收的。

public class MainActivity extends AppCompatActivity {    private static final String TAG = MainActivity.class.getSimpleName();    private int MESSAGE_WHAT = 10000;    Handler handler = new Handler() {        @Override        public void handleMessage(@NonNull Message msg) {            super.handleMessage(msg);            Log.d(TAG, "handleMessage: 我收到消息了");        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        handler.sendEmptyMessageDelayed(MESSAGE_WHAT, 5 * 1000);    }    @Override    protected void onDestroy() {        super.onDestroy();    }}


如上所示,Handler处理的事情很简单, 最终也是没有内存泄漏的。
所以没有绝对的解决方法,具体问题,具体分析

更多相关文章

  1. Android中Cannot draw recycled bitmaps
  2. android 设置seekBar 和Progress背景色
  3. java.lang.RuntimeException: Unable to start activity Compone
  4. LeakCanary原理分析
  5. Android内存泄漏的八种可能(上)
  6. 【Android(安卓)Developers Training】 59. 管理图片存储
  7. android 内存泄漏原因及解决方案
  8. Android中关于退出和Toast的引用
  9. Android之引用包含远程依赖库的aar(引用aar时找不到arr的远程依

随机推荐

  1. Android(安卓)如何实现欢迎界面(Splash Sc
  2. Android中使用注解替代枚举
  3. Android(安卓)color
  4. Android(安卓)Studio 编译卡慢、卡顿的几
  5. Android(安卓)DDMS ADB启动失败错误解决!
  6. android bluetooth蓝牙移植
  7. Android(安卓)绘制一个Loading动画__向图
  8. Android-- Dialog对话框的使用方法
  9. Android2.3 API变更概要
  10. Android(安卓)反编译APK文件