[Android]内存泄露排查实战手记

来源:互联网 发布:超级经典算法大集合 编辑:程序博客网 时间:2024/05/29 17:18

内存泄露排查实战手记

Time:2013.09.02

Author:sodino

问题现象:

这里内存泄露是指已实例化的对象长期被hold住且无法释放或不能按照对象正常的生命周期进行释放。

问题期望:

进行多次重复操作后,能够正常回收该对象(JobAppInterface)。期望在切换帐号后,之前的JobAppInterface能够及时回收(允许等待一段时间后再回收)

问题排查:

经过排查,总结为三种情况导致JobAppInterface内存泄露:

1.静态实例长期占用JobAppInterface

2.线程没有被stop导致JobAppInterface无法释放。

3.Observer/Listener没有被反注册导致ActivityJobAppInterface无法被释放。

一号坑:静态实例长期占用

严重,无法释放

SettingManager的静态实例长期占据第一次初始化的JobAppInterface

表现:启动应用时所创建的JobAppInterface会一直被引用,无法释放。

原因:

经全文查找,发现其静态实例只有初始化的入口,条件是当其实例为null的时候就初始化。sInstance执行new操作后,再没有任何回收操作。这样,除非手Q完全退出了,不然sInstance会一直存在并且占其初始化时使用的JobAppInterface。

public static SettingManager getInstance(AppInterface app) {if (sInstance == null) {sInstance = new SettingManager(app);}return sInstance;}

解决方案:

在获取实例时,如果sInstance已经存在,则对比app,当app不一致时则进行回收和新的实例的生成。

    public static SettingManager getInstance(AppInterface app) {        if (sInstance == null) {            sInstance = new SettingManager(app);        } else if (sInstance.mApp != app) {        //切换了账号sInstance = null;sInstance = new SettingManager(app);}        return sInstance;    }

另,将该实例的回收与JobAppInterface的生命周期保持一致。在JobAppInterface.onDestory()时及时清理掉。

JobAppInterface.javaprotected void onDestroy() {... ...SettingManager.clearInstance(this);... ...}SettingManager.javapublic static void clearInstance(AppInterface app){if(sInstance != null && sInstance.mApp == app){sInstance = null;}}


本文为Sodino所有,转载请注明出处:http://blog.csdn.net/sodino/article/details/17511677


二号坑:线程长期running导致对象无法被释放

严重,无法释放

MessageThread线程一直在运行,导致无法释放JobAppInterface

表现:

这个在疯狂乱点切换帐号时出现。MAT工具发现无法释放的JobAppInterface都有被多个MessageThread引用着。嫌疑很大。但当时对这块逻辑不熟,为了进一步确认,做了如下操作:

1.在生成MessageThread的地方将新new出现的Thread命名为"index_System.time",并在其start()之后输出相应的start日志,日志信息包括Threadname及其所关联的JobAppInterface.hashcode

2.在Thread.run()方法结尾处输出"exit"日志,日志信息同样包括Threadname及其所关联的JobAppInterface.hashcode

经比较,发现快速切换帐号后,每切换一次会新生成5Thread,但最后都只有5Thread会执行"exit"操作,则仍然为(n -1)*5Thread仍在运行着,导致JobAppInterface无法释放。

原因:

确定了MessageThread有问题后,可以下定决心分析下原因了。经查,发现在原逻辑中,是有关闭这些Thread的地方,如下:

   JobInitHandler.java中:   private void onStateRunning() {... ...... ...// 消息拉取完成后,做一些事情        if(curStep == (STEP_GET_MSG + 1)){        doSomethingAfterSyncMsg();//  ---->这里去关闭MessageThread..        }switch (curStep) {        case STEP_INIT:            // action...            break;        case STEP_START:        // action...            break;... ...... ...};}


在JobInitHandler.onStateRunning()"当消息拉取完成后,做一些事情"这里会去把MessageThread 停止掉。但问题就出在这里,关闭的时机出现了问题,在快速切换帐号的操作中,由于没有足够的时间让消息拉取操作完成,也就造成了curStep无法走到值为"(STEP_GET_MSG + 1)"的情形,导致MessageThread一直在空跑无法,其引用的JobAppInterface无法被释放。

解决方案:

在JobInitHandler.destory()时将仍在运行的Thread停止掉。

public void destroy() {... ...... ...// 停止代理处理线程app.getHandler().stopProxyThread();... ...... ...}

三号坑:Observer/Listener没有被反注册

严重,无法释放

注册BroadcastReceiver后没有反注册

表现:

直接看图吧,new的一个BroadcastReceiver在构造函数中直接register,但通篇没有被ungister导致内存泄露。见图1


解决方案:

if(tmpHandler != null && tmpHandler instanceof DataLineHandler){((DataLineHandler)tmpHandler).close();// 执行反注册操作}

问题总结:

就目前来说,经过以上三种类型问题的排查,目前已经达成目的。

这里小总结一下,泄露的原因分别为静态实例占用、线程没被及时停止、注册的Observer/BroadcastReceiver没有及时被反注册。但解决的方法都是同样的:在JobAppInterface的onDesotry()方法(或相似的如JobInitHandler.destory())中及时执行关停回收操作即可避免。



1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 去旅行衣服皱了怎么办 衣服抽绳出来了怎么办 裤子的绑带掉了怎么办 网纱裙的边卷了怎么办 堵奶宝宝吸不通怎么办 棉麻裙子掉毛怎么办 10个月宝宝吃手怎么办 婴儿连体衣长了怎么办 冰丝面料变长了怎么办 t恤袖口大了怎么办 长袖t恤袖子长了怎么办 机打的扣子掉了怎么办 四个月宝宝头扁怎么办 鞋子前面穿翘了怎么办 休完产假没人带怎么办 休完产假孩子吃奶怎么办 巴布豆童鞋里面臭了到底怎么办 连体裤有点卡档怎么办 宝宝连体衣扣子掉了怎么办 买衣服被骂了怎么办? 把人车刮了跑了怎么办 打工打不下去了怎么办 白色衣服变粉了怎么办 一量血压就紧张怎么办 一紧张就血压高怎么办 不在上班时间在单位受伤怎么办 生活过得太压抑怎么办 高低床踏板断了怎么办 高低床 孩子摔下来怎么办 小孩子太小高低床爬梯怎么办 辞职后单位不发工资怎么办 买了个上下床搬家怎么办 爸妈不让学日语怎么办 把共享单车弄坏了怎么办 裙子沾到单车油怎么办? 外穿的短裤卷边怎么办 蹬完单车膝盖疼怎么办 夏天骑摩托太热怎么办 半框眼镜线断了怎么办 镜片从镜框脱了怎么办 干活累的肩膀痛怎么办