Android开发dialog内存泄露,Dialog引发的内存泄漏

66b52468c121889b900d4956032f1009.png

8种机械键盘轴体对比

本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?

本文是本人对于 LeakCanary 团队的一篇分析内存泄漏的文章的意译。水平有限,如有不够准确之处,敬请包涵。

主旨:在Lollipop之前的版本,Dialog可能导致内存泄漏。

引言

LeakCanary 提示存在内存泄漏:

GC ROOT thread com.squareup.picasso.Dispatcher.DispatcherThread.

references android.os.Message.obj

references com.example.MyActivity$MyDialogClickListener.this$0

leaks com.example.MyActivity.MainActivity instance `

这段报告是说:一个 Picasso 线程正持有一个位于栈中的 Message 实例的局部变量,而 Message 持有 DialogInterface.OnClickListener 的引用,而 DialogInterface.OnClickListener 又持有一个被销毁 Activity 的引用。

局部变量由于仅存在于栈内,通常存活时间较短。当线程调用某个方法,系统就会为其分配栈帧。当方法返回,栈帧也会随之被销毁,栈内所有局部变量都会被回收。如果局部变量导致了内存泄漏,一般意味着线程死循环或者阻塞了,而且线程在这种状态时持有着 Message 实例的引用。

于是 Dimitris 和我都去 Picasso 源码中一探究竟:

Dispatcher.DispatcherThread 是一个简单的 HandlerThread:

static class DispatcherThread extends HandlerThread {

DispatcherThread() {

super(Utils.THREAD_PREFIX + DISPATCHER_THREAD_NAME, THREAD_PRIORITY_BACKGROUND);

}

}

这个线程通过 Handler 接收 Message,很标准的实现方式:

private static class DispatcherHandler extends Handler {

private final Dispatcher dispatcher;

public DispatcherHandler(Looper looper, Dispatcher dispatcher) {

super(looper);

this.dispatcher = dispatcher;

}

@Override public void handleMessage(final Message msg) {

switch (msg.what) {

case REQUEST_SUBMIT: {

Action action = (Action) msg.obj;

dispatcher.performSubmit(action);

break;

}

// ... handles other types of messages

}

}

}

显然 Dispatcher.DispatcherHandler.handleMessage() 里面没有明显会让本地变量持有 Message 引用的 Bug。

后来出现了越来越多内存泄漏的报告,这些报告不仅来自 Picasso,各种各样线程中的局部变量都存在内存泄漏,而且这些内存泄漏往往和 Dialog 的 click listener 有关。发生内存泄漏的线程有一个共同的特性:它们都是worker thread,而且通过某种阻塞队列接收各自的工作。

看来问题来自于Handler 和 Thread 的工作机制中。

Handler+Thread 工作原理

HandlerThread也是内部封装了Handler的Thread,让我们看看它的工作原理:

for (;;) {

Message msg = queue.next(); //从消息队列中取出消息,可能阻塞

if (msg == null) {

return;

}

msg.target.dispatchMessage(msg);//对应handler处理消息

msg.recycleUnchecked();//清空msg内容、放回消息池中

}

确实有一个本地变量持有 Message 的引用,但它的生命周期本应很短,而且在循环结束时被清除。

我们尝试通过利用阻塞队列实现一个简单的工作者线程来重现这个 Bug,它只发送一个 Message:

void recycleUnchecked() {

// Mark the message as in use while it remains in the recycled object pool.

// Clear out all other details.

flags = FLAG_IN_USE;

what =

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值