好久没有进cnblogs了,都快长草了.之前对接某度要求我司的插件 monkey test满8小时无OOM 无crash 虐哭了...各种OOM 下面把当时写的一篇笔记po上来防止长草.
1.什么是OOM,为什么会有OOM
Android主要应用在嵌入式设备中,所以因为嵌入式设备本身的一些限制,通常内存都会比较有限.JAVA拥有自己的一套垃圾回收机制,但并不是说用java编写的程序就不会程序溢出.java运行在虚拟机中,虚拟机在初始化时会给它的对内存(Heap)设置一个上限值,android中这个上限值一般是16m.当因为代码缺陷导致内存泄露,泄露的内存被一些数据对象占用着,无法通过GC去回收释放,最终就会导致OOM.
2.怎样检查项目中是否有OOM问题
要检查项目中是否有OOM问题,可以借助eclipse中的DDMS和MAT去分析观察内存.我们可以这样做:
1)把手机的开发者选项模式打开并用数据线连接电脑(废话)
2)打开eclipse切换到DDMS视图如下图
3)击选中 Devices 视图界面中最上方一排图标中的“Update Heap”图标:
接着手机进入应用要测试排查是否有泄露的activity或者fragment,之后在DDMS右边的视图点击一下Cause GC,让虚拟机强制GC一次,之后就不用点了,它会自动刷新视图(点击以后可能会卡住一段时间这属于正常的).然后观察GC后的data ovhect的 Total size和Count.尝试关闭当前页面并重新进入,反复进行如此操作后看Count和Total size在GC后会不会有明显回落接近之前GC后的值,如果多次操作GC后内存呈现较大的增加趋势那么,这个页面可能会有问题!!
或者也可以尝试使用MAT内存分析工具去分析内存泄露的原因,我功力尚浅只是浅尝即止有兴趣可以根据这篇教程去看
http://rayleeya.iteye.com/blog/1956638
3.怎样去解决OOM
我所碰到的OOM问题暂时就这三种情况
1)在一些单例的构造方法中需要传入一个context,传入了activity的context,导致单例持有这个activity的引用不能让其回收.
例如操作数据库的UserDatabase类,它是单例的主要用来打开关闭数据库存放数据,我们项目中之前写得人忽略了这一点,传入的context是acitivity的,因为单例的生命周期和整个程序的生命周期一样长,建议直接使用Application的context.
2)静态的成员变量
有时因为一些原因(比如希望节省Activity初始化时间等),将一些对象设置为static的,我们会忘了在activity退出的时候把这些变量的引用给释放,所以导致和这个变量相关的Activity强引用的其他对象也无法被释放,这样就造成了内存泄露.建议在onDestroy方法中把这些static的变量给置为null;
3)注册/取消监听对象
经常要用到一些XxxListener对象,或者是XxxObserver、XxxReceiver对象,然后用registerXxx方法注册,用unregisterXxx方法注销。本身用法也很简单,但是从一些实际开发中的代码来看,仍然会有一些问题: registerXxx和unregisterXxx方法的调用通常也和Cursor的打开/关闭类似,在Activity的生命周期中成对的出现即可:
在 onCreate() 中 register,在 onDestroy() 中 unregitster;
在 onStart() 中 register,在 onStop() 中 unregitster;
在 onResume() 中 register,在 onPause() 中 unregitster;
最近碰到的问题,在fragment中定义了一个PhoneStateListener的对象,将其注册到TelephonyManager中:
TelephonyManager.listen(l,PhoneStateListener.LISTEN_SERVICE_STATE);
但是在Activity退出的时候注销掉这个监听,即没有调用以下方法:
TelephonyManager.listen(l,PhoneStateListener.LISTEN_NONE);
因为PhoneStateListener的成员变量callback,被注册到了TelephonyRegistry中,TelephonyRegistry是后台的一个服务会一直运行着。所以如果不注销,则callback对象无法被释放,PhoneStateListener对象也就无法被释放,最终导致Activity对象无法被释放
参考资料:
http://rayleeya.iteye.com/blog/1956059