[Android]内存泄露排查实战手记
来源:互联网 发布:超级经典算法大集合 编辑:程序博客网 时间:2024/05/29 17:18
内存泄露排查实战手记
Time:2013.09.02
Author:sodino
问题现象:
这里内存泄露是指已实例化的对象长期被hold住且无法释放或不能按照对象正常的生命周期进行释放。
问题期望:
进行多次重复操作后,能够正常回收该对象(JobAppInterface)。期望在切换帐号后,之前的JobAppInterface能够及时回收(允许等待一段时间后再回收)。
问题排查:
经过排查,总结为三种情况导致JobAppInterface内存泄露:
1.静态实例长期占用JobAppInterface。
2.线程没有被stop导致JobAppInterface无法释放。
3.Observer/Listener没有被反注册导致Activity或JobAppInterface无法被释放。
一号坑:静态实例长期占用
严重,无法释放
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日志,日志信息包括Thread的name及其所关联的JobAppInterface.hashcode。
2.在Thread.run()方法结尾处输出"exit"日志,日志信息同样包括Thread的name及其所关联的JobAppInterface.hashcode。
经比较,发现快速切换帐号后,每切换一次会新生成5个Thread,但最后都只有5个Thread会执行"exit"操作,则仍然为(n -1)*5个Thread仍在运行着,导致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())中及时执行关停回收操作即可避免。
- [Android]内存泄露排查实战手记
- 内存泄露排查实战手记
- Android内存泄露排查
- Android 内存泄漏排查实战
- [android]_[内存泄露分析和排查]
- 内存泄露的排查
- 内存泄露排查
- nodejs内存泄露排查
- 内存泄露排查
- 循序渐进学用MAT排查Android Activity内存泄露
- android 内存泄露原因以及排查和解决方案
- LeakCanary-------快速简单排查Android中的内存泄露
- pyclutter内存泄露排查记录
- Java内存泄露排查备忘
- 内存泄露之排查篇
- c++内存泄露排查思路
- android开发游记:性能测试中内存泄露排查方法与防止泄露编码心得
- 一次内存泄露问题的排查
- MVC实现EF模型随意切换(四)学习
- 读书笔记写作方法
- xml的sax解析
- cache一致性协:MESI和MOESI
- 嵌入式Linux之我行——u-boot-2009.08在2440上的移植详解(五)
- [Android]内存泄露排查实战手记
- Android 自动化测试用例规范
- Linux管理日记(一)
- hadoop yarn安装
- 轻松下载虾米音乐,不话费任何虾币
- 01背包问题
- Cocos2d-x全屏适配问题
- 黑马程序员-关于类的继承问题与final关键字的一些感悟
- Python与Shell之间变量的传递