Android Studio 内存泄漏分析指南
最近在优化公司的项目,想针对内存泄漏进行分析一下,查阅网上的一些资料,发现苦逼的是,资料一大堆,但是内容都差不多,至今未找到一篇实战的内存泄漏分析,全部停留在理论知识上,最后查阅了一些外文文献,才有了一点思路,好吧!既然没有实战分析,我就做第一人,把我学到的分享出来,希望对你们有帮助,当然有说的不对的,望各位指出来~
内存泄漏原因
当应用不需要某对象时候,忘记释放分配的内存,该对象仍然保持被引用状态(当对象拥有强引用,GC无法回收),从而导致内存泄漏
泄漏的源头
泄漏的源头有很多,有开源的第三方框架引起的、android系统自身造成的如webview的内存泄漏,还有一个是我们可以控制的就是自身编码引起的内存泄漏,这也是我们可以避免的,一下情况容易出现内存泄漏
一、 Context 引起的内存泄漏
常见问题:
这个是最常见的,因为Activity经常要用到上下文Context,很有可能Activity作为Context传递给某些类,Activity生命周期结束之后,某些类仍然存活并保持着该Activity的引用,保持引用就无法被回收。从而导致内存泄漏
解决方案:
- 下次如果要传入context给某些类的时候,最好是用ApplicationContext,这样可以有效避免内存泄漏
- 在界面销毁的时候,手动释放掉引用过activity的context
二、Static 静态变量
常见问题:
我们有时候会为了方便,设置某个Activity或者View为静态变量,但是你要知道,static变量是要贯穿整个应该的生命周期,就是意味着即使Activity销毁,static变量也不会销毁,所以,如果你把View或者Activity设置为静态变量,这会导致当前的Activity会一直存在,从而导致内存泄漏
解决方案:
- 尽量避免使用static变量
- 如果非要使用的话,记得在界面销毁的时候,把静态变量也释放掉,简单说就是把静态变量置空,如staticView=null
三、非静态内部类、匿名内部类持有外部类的引用
常见问题:
我们在使用AsyncTash、Handler、TimerTask、Thread的时候,为了方便,直接new一个匿名内部类对象,殊不知我们在new一个的时候,编译器在编译的时候会自动为内部类的构造方法加上外包类的引用,所以这些匿名内部类会持有Activity,当这些匿名内部类处理非常耗时的操作时候,就算Activity生命周期结束,也不会被销毁,这就会造成内存泄漏
解决方案:
- 当Activity销毁的时候,对不需要的匿名内部类进行任务停止操作
- 使用静态内部类,静态内部类内部使用弱引用来引外外部类,这样当Activity销毁的时候,弱引用是可以被回收的
四、其他引起的泄漏
- 我们在使用系统服务的时候,比如注册了一些广播监听,在使用完后,要释放掉
- 我们在给View绘制动画的时候,View被动画持有,而Activity又被View持有引用,导致Activity也无法释放。所以在Activity销毁时,调用animator.cancel()来停止动画
内存泄漏分析-实战篇
这个才是尽头的重头戏,前面写的只是对网上文献的一些总结
分析工具
- MAT :eclipse时代的分析工具,具体分析实战可以参考这篇文章
- LeakCanary: Square开源的内存泄漏探测器,具体使用参考这篇文章
- Android Monitor:Android Studio 自带的分析工具,今天的重头戏
Android Monitor
-
生成HPROF文件,点击红框中有绿色下载箭头的按钮(Dump java Heap),这个会生成以hprof结尾的文件
Android Monitor.png -
生成的效果如下图,简单介绍一下界面功能
功能描述:
名称 | 描述 |
---|---|
Total Count | 该类的实例总数 |
Heap Count | 所选择的堆中该类的实例的数量 |
Sizeof | 单个实例所占空间大小(如果每个实例所占空间大小不一样则显示0) |
Shallow Size | 堆里所有实例大小总和(Heap Count * Sizeof) |
Retained Size | 该类所有实例所支配的内存大小 |
Instance | 具体的实例 |
Reference Tree | 所选实例的引用,以及指向该引用的引用。 |
Depth | GC根节点到所选实例的最短路径的深度 |
Shallow Size | 所选实例的大小 |
Dominating Size | 所选实例所支配的内存大小 |
- 也许你看完这些也还是一头雾水,不知道怎么分析,没关系,我们不就是要找到内存泄漏的源头嘛,来看这里,要开始放大招啦,看到上图中最右边的 Analyzer Tasks,点击这个版块,这个版块就是主要的查找内存泄漏版块
- 启动页很简单,点击绿色运行按钮就好,之后会在下面列出可能存在内存泄漏的Activity,如果没显示,说明启动的Activity中没有内存泄漏
- 查看引用树(Reference Tree),都是根据这个查找内存泄漏的具体地方的
- 一般有类似三角形的,就是一直被引用这,导致内存泄漏,找到对应的地方也很简单,右键项目点击Jump to Source就会打开对应泄漏源码的地方啦,然后我们在根据上面说的泄漏原因,及解决办法修正就好啦
上面一共有五六处内存泄漏,都是我故意写出来的,目的就是为了练习排查分析内存泄漏,特地放上源码,demo中内存泄漏以及修复代码都有,欢迎下载学习,如果觉得不错,顺手给个star