This post is a permitted translation of badoo Tech Blog and I add some text and screenshots for android studio users.
Origin Author: Dmytro Voronkevych
follow badoo on Tweet
Translator: Miao1007截至androidstudio1.3为止,其内部的MemoryDump功能都很难使用,还是使用MAT更佳。
Android使用java作为平台开发,帮助了我们解决了很多底层问题,比如内存管理,平台依赖等等。然而,我们也经常遇到OutOfMemoey
问题,垃圾回收到底去哪了?
接下来是一个Handler Leak
的例子,它一般会在编译器中被警告提示。
public class NonStaticNestedClassLeakActivity extends ActionBarActivity {
TextView textView;
public static final String TAG = NonStaticNestedClassLeakActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_non_static_nested_class_leak);
textView = (TextView)findViewById(R.id.textview);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override public void
textView.setText("Done");
}//a mock for long time work
}, 800000L);
}
}
这是一个非常基础的Activity.注意这个匿名的Runnable
被送到了Handler中,而且延迟非常的长。现在我们运行这个Activity,反复旋转屏幕,然后导出内存并分析。
MAT是对java heap中变量分析的一个工具,它可以用于分析内存泄露。
OQL
图标select * from instanceof android.app.Activity
并按Ctrl + F5
或者!
按钮在OQL(Object Query Language)窗口下输入的查询命令可以获得所有在内存中的Activities,这段查询代码是不是非常简单高效呢?
点击一个activity对象,右键选中Path to GC roots
在打开的新窗口中,你可以发现,你的Activity是被this$0</code>所引用的,它实际上是匿名类对当前类的引用。<code>this$0
又被callback
所引用,接着它又被Message
中一串的next
所引用,最后到主线程才结束。
任何情况下你在class中创建非静态内部类,内部类会(自动)拥有对当前类的一个强引用。
一旦你把Runnable
或者Message
发送到Handler
中,它就会被放入LooperThread
的消息队列,并且被保持引用,直到Message
被处理。发送postDelayed这样的消息,你输入延迟多少秒,它就会泄露至少多少秒。而发送没有延迟的消息的话,当队列中的消息过多时,也会照成一个临时的泄露。
现在把Runnable
变成静态的class
现在,摇一摇手机,导出内存
为什么又出现了泄露呢?我们看一看Activities
的引用.
看到下面的mContext
的引用了吗,它被mTextView
引用,这样说明,使用静态内部类还远远不够,我们仍然需要修改。
现在我们把刚刚内存泄露的罪魁祸首 - TextView改成弱引用。
再次注意我们对TextView保持的是弱引用,现在让它运行,摇晃手机
小心地操作WeakReferences,它们随时可以为空,在使用前要判断是否为空.
哇!现在只有一个Activity的实例了,这回终于解决了我们的问题。
所以,我们应该记住:
如果你把现在的代码与开始的代码相比,你会发现它们大不相同,开始的代码易懂简介,你甚至可以脑补出运行结果。
而现在的代码更加复杂,有很多的模板代码,当把postDelayed
设置为一个短时间,比如50ms
的情况下,写这么多代码就有点亏了。其实,还有一个更简单的方法。
Handler可以使用removeCallbacksAndMessages(null)
,它将移除这个Handler所拥有的Runnable
与Message
。
//Fixed by manually control lifecycle
@Override protected void onDestroy() {
super.onDestroy();
myHandler.removeCallbacksAndMessages(null);
}
现在运行,旋转手机,导出内存
Good!只有一个实例。
这样写可以让你的代码更加简洁与可读。唯一要记住的就是就是要记得在生命周期onDestory的时候手动移除所有的消息。
(这个是第三方库,我就不翻译了,大家去Github上去学习吧)
在Handler中使用postDelayed
需要额外的注意,为了解决问题,我们有三种方法
这三种你可以任意选用,第二种看起来更加合理,但是需要额外的工作。第三种方法是我最喜欢的,当然你也要注意WeakHandler不能与外部的强引用共同使用。
本博客将长期保持原创性,翻译文章费时费力,如果你认为我的免费劳动有价值的话,不妨帮忙点赞
或者关注我
吧!