最近半年做了比较多的内存优化工作,虽说现在网上关于内存优化的博客一抓一大把,而且确实很多都写的很棒总结的很好,但还是忍不住想要将自己的一些心得分享出来,会有点罗嗦,但看完应该还是有点收获的。
什么是内存优化,什么时候需要内存优化,内存优化的目的是什么
内存优化就是优化内存嘛,让应用占用的内存更小嘛,让用户使用起来更流畅,占用的资源更小,而且最重要的是内存不会无限制增大导致OOM。所以,当你的应用出现了OOM,这绝对是需要进行内存优化了!不过需要注意的是出现OOM的地方不一定是内存泄露的地方,可能是其他地方内存将内存占满了,刚好这个地方申请内存的时候内存不够了在这个地方报了OOM。
很多开发者在出现OOM的时候知道需要优化内存了,但其他时候完全没有优化内存的意识,实际上内存优化是一个长久的工作,不是说要出了问题才去优化,而是要时刻去想着优化,尽量让应用的内存做到最低。尤其是在有比较大的改动或者版本发布的时候,一定要去关注对比下之前的内存变化,而且,要时刻关注自己跟竞品的内存对比。。。这个很重要。。。还有,不是说不OOM就没有内存泄漏了,内存泄漏是说有不需要保存的资源却还没有释放,仍然在占用内存,不一定会导致OOM,而且native层的内存泄漏是不会报OOM的。所以,千万不要等到OOM了才想去优化内存,要时刻去关注自己应用的内存,至少在版本发布前或者有大的改动的时候一定要关注一下。
那内存优化的目的是什么?内存优化就是内存优化啊,内存小了就!是!好!
内存优化常用定位工具
就跟解bug一样,大部分的bug都是定位的时候比较困难,实际解决的时候并不需要费很大的功夫,内存优化的关键也是在于寻找定位可以进行内存优化的点。已有的很多开源库跟工具都可以帮我们来定位到优化的点。我主要介绍以下三种:
- LeakCanary,默认是会对activity等组件进行监控的,建议对fragment及自己项目中用到的一些重要的对象也进行监听。它这个监测的原理很简单,就是发现这个对象在该释放的时候没有释放,就会有自动提示并且显示出具体的引用关系。这个集成比较简单而且只会花费集成时的一点工作量,所以强烈建议集成到自己的项目中。
- MAT(Memory Analyzer Tool),神器,JAVA层内存泄漏最常用的定位工具,绝大部分的JAVA层内存泄漏都可以通过MAT快速发现并且定位出来。MAT可以将内存占用比较大的Objects罗列出来,可以按照对象的数目或者大小排列,可以正则匹配搜某些类的占用数量,可以对比两个时间点的数目变化,可以看某个对象或者类的引用关系,等等等等。总之。这个东西是个神器,而且稍微百度一下就一大堆MAT的使用方法。另外推荐去Eclipse 官方Help网址 Memory Analyzer看下官方的MAT帮助文档,至少一些关键的概念要清楚。
- 命令行查看内存占用,可以查看内存的命令有procrank、dumpsys meminfo、dumpsys meminfo packageName。建议使用最后一种,例如你的包名是com.example.helloworld,则使用dumpsys meminfo com.example.helloworld查看它的内存。因为dumpsys meminfo packageName 不仅仅打印出了总的内存占用情况,还将内存的具体分布情况也打印出来了,同时还有View、Binder、Activity等关键通用Object的数据。
注意:介绍这三种方式并不是让你们去选择一种来协助定位,而是建议把三种结合起来一起用。通常情况下LeakCanary是要直接集成到项目中来帮我们自动监测一些重点关注的对象,而命令行跟MAT是在需要分析内存的时候,通过命令行来查看内存的整体使用情况,然后决定在什么时候抓取hprof文件,再用MAT进行分析定位出异常产生的原因。
PS:MAT只能看到Dalvik Heap的内存泄漏,native层的虽说可以通过命令行看到内存占用大小,可以对比一些时间点或者一些操作后是否有内存泄漏,但是定位具体的内存泄漏的地方没听说过什么特别好的办法(也可能是因为我对native层接触的比较少所以孤陋寡闻吧)。不过可以提供一种思路,因为我们可以通过ls -al /proc/进程号/fd|busybox wc -l
得到某个进程的fd数量,可以通过这种办法看使用到了fd的地方有没有泄露,例如socket、pipe等。
内存优化重点关注点
实际上内存优化定位及修改还是比较简单的,只要合理使用上面提到的几个工具,JAVA层的内存泄漏应该大部分都很容易能够定位到。在这里总结一下常见的并且比较重要需要关注的内存优化点:
- Activity、Fragment或者一些View未释放。这些可以通过LeakCanary或者命令行很容易发现出来。而且因为一般包含ImageView等资源,所以优化效果很明显。
- 图片内存优化。对大部分应用来说图片是占用内存比较大的一块,也是优化空间比较大的一块。常见的优化方式是需要显示时再到内存;图片分辨率及大小在不印象UI的情况下尽量小;修改图片加在框架的缓存大小、图片质量和缓存方式等。
- 随着用户操作或者时间会持续增长的资源。这类如果存在内存泄漏或者没有及时回收,可能会导致内存会显著增加,因此也是重点关注的对象。典型的例子就是ListView,如果使用方式不对,随着ListView的上下滑动,内存会持续增加直到OOM。跟这个类似的还有pipe或者socket读写一些数据啊、播放音视频文件呀等等等等。
- 本身占用内存比较多的地方。要去重点关注是否有内存泄漏,没有内存泄漏时也要去想这个东西是否需要常驻,如果重新加载的话需要多久的时间,是否对用户体验有影响等等。
- 使用到了第三方API或者系统资源的地方,一定要考虑这个接口使用到的资源需不需要释放,例如HandlerThread。
- 不合理的需求跟UI。不是在开玩笑,程序员不要把自己的目光仅仅局限在技术跟代码中。如果因为不合理的产品需求或者过度设计的UI导致内存始终降不下去,那不如考虑跟他们协商是否有必要更改UI或者需求。而且,最好在产品设计阶段就去介入,避免这种问题的产生。
好了,就写这么多了,说是技术博客,其实更像一个随笔一样把自己的感悟分享出来吧,如果有疑问或者有不同意见,欢迎来一起探讨~