话说,从mta上报的数据上来看,我们的app出现了3起OOM(out of memery):
java.lang.Throwable: java.lang.OutOfMemoryError
at com.tencent.stat.a.d.(Unknown Source)
at com.tencent.stat.g.uncaughtException(Unknown Source)
at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:693)
at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:690)
at dalvik.system.NativeStart.main(Native Method)
从错误堆栈是显然是看不出任何问题的,那么问题会出现在什么地方呢?
我们猜测了一下几种可能?
1、存在内存泄露。
2、不需要的bitmap没有被释放。
那么问题是否就是上面这两种情况呢?
带着这个问题,我们首先验证下,内存是否占用真的很大。
首先,开启android studio内存占用图标展示工具,可以看到内存占用77M,
重启应用,各页面点击一下,相关路径踩一踩,不踩不知道,一踩就吓一跳,启动的时候,直观的从图中可以看到,内存占用40M,然而,切换到发现tab,发现内存彪到70M,然后,切换其他页面,如下图:
比如切换到,消息,我的,等等,发现内存依然,居高不下了,滑动发现页的时候,内存占用依然有标高的趋势。
我们使用 dumpsys meminfo com.xxx.xxx (app包名),查看内存占用情况如下图:
cat 一下进程,可以看到最大占用(这里包括虚拟机和原生)如下图:
不用说了,这个的优化,那么怎么办?
依然是寄出我们android studio上的内存分析工具,如下图:
首先,GC一下 ,然后在导出hprof文件,进行分析 。首先分析是否存在内存泄露:
如上图,并没有发现有内存泄露的activity存在,这也归功于我们平常有事没事都会随手分析一下是否有内存泄露,如果有,早就解决了,等不到我。
那么既然没有内存泄露的Activity,那么我们何不看一下对象的内存占用情况呢?
如是:如图
这里,我们很轻易的抓住了第一个凶手,bitmap。
其原因是因为这里做了一个打分的自定义view,这个view的分数是绘制出来的,之所以没有用字体,是因为引入一个字体库会增加包大小(虽然可以有些工具可以抽取仅仅需要的字体元素来缩小字体库,但我们考虑到实现一个打分效果的自定义view的成本也不大,因此并没有考虑抽取字体库),得不偿失,然而,这个自定义view中每个实例,都拥有0到9加上。的bitmap;
然而他被显示到列表的时候,可想而知,会有多少张图,优化起来相当简单,将这些bitmap使用静态变量保存,这样所有实例只会公用一份bitmap列表了:
,好吧,继续走查其他对象的内存占用,我们发现:
PopoFeed对象占用内存也较多:但,一层一层的剥下去,最终发现是PopoFeed对象中的User对象里面的UserTag占用内存较多:
可以想象,一个popofeed如果有20多条评论,10几个人打分,那这样就有30个User对象在popofeed对象中,30*3K,那就是90K,这些对view展示无用的数据吃内存也是非常恐怖的,所以,拉起后台同学,对返回的数据做了优化。同时,我们发现,返回的数据多,GSON转model所需要的内存也较多,所以,服务端对返回数据做清洗还是挺有必要的。
,好吧,到了第三阶段,我们继续走查,发现fragment占用内存较多,其实不难推测,使用fragmentManger管理fragment ,你看到的是一个页面,但其实上,默认是会加载1-2个到缓存中的,从源码中可以看出:
所以,你当前在发现页,实际上,大厅,消息都已经加载进内存了,那么这时候的做法就是在重写setUserVisibleHint
方法,当fragment可见的时候,将数据渲染到view上,当fragment不可见的时候,把view上数据清理掉,不过或许也有更好的方法,如果有,欢迎告诉我~。
然而,还有一个更加可恶的问题,那就是当fragment执行onDestroyView方法后,该fragment并没有释放掉内存,这也就是为什么切换到发现之后,在切换其他fragment内存居高不下的首要原因,我的解决办法是:
因为从引用树上看到:
findFragment被fragmentManger引用着,其在执行onDestroyView的时候,一些该释放的内存得不到释放,因此采取以上办法。
好吧,经过三个小小的优化,我们来看看,内存占用:
对比发现页:
发现页内存占用现在是 48M,同比之前的77M,减少了37%。
然后切换到我的
我们发现内存占用只有28.58M。
对于内存峰值方面的对比,
(165516-120792)/165516 = 27%
总结这次的优化:
1、当内存中类的多个对象引用的资源不变的时候,请使用静态,这样,这些资源就只有一份,减少不必要的内存占用。
2、json转model是一个很耗时的过程,减少json中不必要的字段,不仅可以加快json转model的时间,还能降低内存占用。
3、fragment不可见的时候,实际上可能还占用着你的内存,要懂得小心释放不必要的内存。
腾讯自主研发,荣获2015年十佳组件第一名的“tMemoryMonitor”内存泄漏分析工具。该腾讯内部工具已经在腾讯WeTest官网内免费开放给用户使用
TMM下载地址:http://wetest.qq.com/cloud/index.php/index/TMM
【工具简介】
tMemoryMonitor简称TMM,是一款运行时C/C++内存泄漏检测工具。TMM认为在进程退出时,内存中没有被释放且没有指针指向的无主内存块即为内存泄漏,并进而引入垃圾回收机制,在进程退出时检测出堆内存中所有没有被引用的内存单元,因而内存泄漏检测准确率为100%。