记一次Android OOM问题的解决

一个困扰了我一个多礼拜的OOM bug今天终于给解决掉了,前辈攻城狮说OOM这种东西总是由不起眼的东西引起的,此言不虚啊!
最初客户回报上来的症状是程序莫名卡死、黑屏,无法操作乃至需要重启设备才能恢复正常( 这里我学到了一个道理:客户都是没什么耐心的。 其实多等几秒系统杀掉程序出现FC对话框就不需要重启设备了的说)。

起初我希望能重现bug然后根据Logcat的报告定位错误,但是尽管我用尽办法各种虐待我的GN,总是无法重现问题。这样没头没脑地耗了两天(期间还在做别的代码,也没浪费时间),于是在一个闷热的下午,程序媛我背上背包出了现场。(为了出这个现场还差点把自己笔记本的网卡们搞死,真是辛苦它们了。)
总觉得我有bug退散体质的说,在客户店里从下午坐到晚上一直风平浪静,倒是刚到那里的时候随便点了两下就在Logcat里捕捉到了一次OOM。
晚上九点收集了一下服务员们的使用体验并征求老板同意带回了一台设备准备进一步查找问题。

第二天一早刚开始研究就又出现OOM了,这时候我反省了一下盲目寻找容易引起OOM的代码模式加以修改的做法,开始用内存分析工具武装我的Eclipse,装了MAT插件。
接下来各种测试各种看内存分析报告,首先是了解了GN不能重现黑屏问题的原因——丫为一个App分配了四五十兆的内存的情况下还是能蹒跚而行,而客户用的采用三星定制Rom的设备可没有如此大的胸怀。不过至少GN也是会有潜在OOM的可能的,我可以把客户的设备还回去了。

能够定位到无法GC的Activity有一大半要归功于运气,我就是在无数的类列表里看到了它!其实它本身占用的内存并不大,根本排不到前面,我几乎是在绝望的时候漫无目的地随手展开着内存报告的列表的时候注意到了它出现了多个实例!虽然一个Activity占用的内存不大,但是一个Activity里会有很多控件,这样一套下来的内存占用也很可观了!
自此我开始专注于这个Activity内部寻找引起OOM的代码,这里离最终解决其实还是有很大差距的,但是内容我就不详述了,不外乎是各种二分法缩小问题代码范围。
最终找到的原因是我在一个Dialog的布局xml里使用了<requestForcs />这一句,去掉以后各种正常。

兴奋过后回顾在今天下午定位到这一句之前走过的一些弯路,突然发现有一点并不能很好的得到解释:
我曾经试过注释掉那个Acitivity里几乎所有的代码(为什么说是几乎所有呢?后面解释),然后更换Activity的指向的界面xml,同样内容的两个xml文件(都不是最初引起不释放的那个布局文件)却一个能够释放一个不能释放;这个诡异的现象在我去掉这个Activity主题中的android:windowIsTranslucent属性后消失了,所以我曾经一度认为是这个属性引起的bug,不过最后找到问题所在以后做的回归测试给它平了反。
虽然现在问题得到了解决,但是这个查找问题过程中碰到的诡异现象实在是有些匪夷所思。没错,我前面所说的注释掉的“几乎所有”的代码中,并不包括引起问题的代码,那一份错误的xml在我为Acitivity更换各种xml的过程中始终都是会被加载的——我在一个自定义对话框的构造函数中inflate了它,而new那个对话框的语句在我更换Activity的布局文件过程中始终在运行着。
Maybe,我只是眼花了。