在android开发过程当中,咱们可能会遇到过使人奔溃的OOM异常,面对这样的异常咱们是既熟悉又深恶痛绝的,由于形成OOM的缘由有不少种状况,如加载图片过大,某已再也不使用的类未被GC及时回收等等……本篇咱们就来分析其中一种形成OOM的场景,它就是罪恶的内存泄漏。对于这样的称呼,咱们并不陌生,甚至多次与之"并肩做战",只不过它就是一个猪队友,只会不断送塔…….java
本篇分为3部分:android
1.Handler内存泄漏例子说明以及原理阐明
2.问题验证(若是感受繁琐请直接跳过)
3.Handler内存泄漏解决方法
eg.Handler内存泄漏例子说明以及原理阐明web
Handler,咱们已经至关熟悉了,并且常常用得不亦乐乎,但就是由于太熟悉了,才会偶尔被它反捅一刀,血流不止……还记得咱们曾经满怀信心地使用着以下的优美而又简洁的代码不?数组
不怕你吓着,实话告诉你,这个代码已经形成内存泄漏了!!!不相信?咱们使用Android lint工具检测一下该类的代码:app
面对现实吧,那为何会这样呢?在java中非静态内部类和匿名内部类都会隐式持有当前类的外部引用,因为Handler是非静态内部类因此其持有当前Activity的隐式引用,若是Handler没有被释放,其所持有的外部引用也就是Activity也不可能被释放,当一个对象一句不须要再使用了,原本该被回收时,而有另一个正在使用的对象持有它的引用从而致使它不能被回收,这致使本该被回收的对象不能被回收而停留在堆内存中,这就产生了内存泄漏(上面的例子就是这个缘由)。最终也就形成了OOM
…….咱们再来段清晰的代码,咱们来使用mHandler发送一个延迟消息:dom
分析:当咱们执行了HandlerActivity的界面时,被延迟的消息会在被处理以前存在于主线程消息队列中5分钟,而这个消息中又包含了Handler的引用,而咱们建立的Handler又是一个匿名内部类的实例,其持有外部HandlerActivity的引用,这将致使了HandlerActivity没法回收,进行致使HandlerActivity持有的不少资源都没法回收,从而就形成了传说中的内存泄露问题!ide
为了进一步验证内存泄漏问题,咱们在该类中建立一个int数组,该数组分配的内存大小为2m,而后咱们用DDMS来查看heap内存,而后使用GC回收,看看内存会不会有变化:工具
第一次启动app时,head内存为12.5M,已经分配内容(Allocated):8.5M,空闲内存:4M,咱们频繁点击GC按钮,内存并无发生明显变化,如今咱们点击手机返回健,推出应用,而后再从新进入,一样检测一下head内存:post
咱们发现head内存:20.5M,Allocated:16.5M,Free:4M,堆内存和已经分配内存近乎翻倍,咱们继续频繁点击GC, 看看可否被回收?结果内存并无明显变化,如今咱们继续点击手机返回健,推出应用,而后再从新进入,一样再次检测一下head内存:this
咱们发现head内存:28.5M,Allocated:24.5M,Free:4M,堆内存和已经分配内存又增长了,并且不管咱们如何点击GC回收内存,内存都没有明显变化,并且每启动一次该页面,内存就增长一倍!这也就说存在在某个类只建立而没销毁的状况,其实就是存在内存泄漏问题。咱们使用MAT工具进一步验证这个问题,咱们来看一组Histogram的数据和dominator tree数据,首先是Histogram的数据:
dominator tree数据:
同时存在三个同样的HandlerActivity和内部类,这就足以说明HandlerActvity只有建立没被销毁了吧,也就是说Handler形成的内存泄漏真的存在。
解决这个问题思路就是使用静态内部类并继承Handler时(或者也能够单独存放成一个类文件)。由于静态的内部类不会持有外部类的引用,因此不会致使外部类实例的内存泄露。当你须要在静态内部类中调用外部的Activity时,咱们可使用弱引用来处理。另外关于一样也须要将Runnable设置为静态的成员属性。修改后不会致使内存泄露的代码以下:
方法:声明静态匿名内部类+弱引用
public class HandlerActivity extends Activity {
//建立一个2M大小的int数组
int[] datas=new int[1024*1024*2];
// Handler mHandler = new Handler(){
// @Override
// public void handleMessage(Message msg) {
// super.handleMessage(msg);
// }
// };
/**
* 建立静态内部类
*/
private static class MyHandler extends Handler{
//持有弱引用HandlerActivity,GC回收时会被回收掉.
private final WeakReference<HandlerActivity> mActivty;
public MyHandler(HandlerActivity activity){
mActivty =new WeakReference<HandlerActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
HandlerActivity activity=mActivty.get();
super.handleMessage(msg);
if(activity!=null){
//执行业务逻辑
}
}
}
private static final Runnable myRunnable = new Runnable() {
@Override
public void run() {
//执行咱们的业务逻辑
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_leak);
MyHandler myHandler=new MyHandler(this);
//解决了内存泄漏,延迟5分钟后发送
myHandler.postDelayed(myRunnable, 1000 * 60 * 5);
}
}
复制代码