Android Studio 内存泄漏分析指南

最近在优化公司的项目,想针对内存泄漏进行分析一下,查阅网上的一些资料,发现苦逼的是,资料一大堆,但是内容都差不多,至今未找到一篇实战的内存泄漏分析,全部停留在理论知识上,最后查阅了一些外文文献,才有了一点思路,好吧!既然没有实战分析,我就做第一人,把我学到的分享出来,希望对你们有帮助,当然有说的不对的,望各位指出来~

内存泄漏原因


当应用不需要某对象时候,忘记释放分配的内存,该对象仍然保持被引用状态(当对象拥有强引用,GC无法回收),从而导致内存泄漏

泄漏的源头


泄漏的源头有很多,有开源的第三方框架引起的、android系统自身造成的如webview的内存泄漏,还有一个是我们可以控制的就是自身编码引起的内存泄漏,这也是我们可以避免的,一下情况容易出现内存泄漏

一、 Context 引起的内存泄漏

常见问题:

这个是最常见的,因为Activity经常要用到上下文Context,很有可能Activity作为Context传递给某些类,Activity生命周期结束之后,某些类仍然存活并保持着该Activity的引用,保持引用就无法被回收。从而导致内存泄漏

解决方案:

  1. 下次如果要传入context给某些类的时候,最好是用ApplicationContext,这样可以有效避免内存泄漏
  2. 在界面销毁的时候,手动释放掉引用过activity的context

二、Static 静态变量

常见问题:

我们有时候会为了方便,设置某个Activity或者View为静态变量,但是你要知道,static变量是要贯穿整个应该的生命周期,就是意味着即使Activity销毁,static变量也不会销毁,所以,如果你把View或者Activity设置为静态变量,这会导致当前的Activity会一直存在,从而导致内存泄漏

解决方案:

  1. 尽量避免使用static变量
  2. 如果非要使用的话,记得在界面销毁的时候,把静态变量也释放掉,简单说就是把静态变量置空,如staticView=null

三、非静态内部类、匿名内部类持有外部类的引用

常见问题:

我们在使用AsyncTash、Handler、TimerTask、Thread的时候,为了方便,直接new一个匿名内部类对象,殊不知我们在new一个的时候,编译器在编译的时候会自动为内部类的构造方法加上外包类的引用,所以这些匿名内部类会持有Activity,当这些匿名内部类处理非常耗时的操作时候,就算Activity生命周期结束,也不会被销毁,这就会造成内存泄漏

解决方案:

  1. 当Activity销毁的时候,对不需要的匿名内部类进行任务停止操作
  2. 使用静态内部类,静态内部类内部使用弱引用来引外外部类,这样当Activity销毁的时候,弱引用是可以被回收的

四、其他引起的泄漏

  1. 我们在使用系统服务的时候,比如注册了一些广播监听,在使用完后,要释放掉
  2. 我们在给View绘制动画的时候,View被动画持有,而Activity又被View持有引用,导致Activity也无法释放。所以在Activity销毁时,调用animator.cancel()来停止动画

内存泄漏分析-实战篇


这个才是尽头的重头戏,前面写的只是对网上文献的一些总结

分析工具

  1. MAT :eclipse时代的分析工具,具体分析实战可以参考这篇文章
  2. LeakCanary: Square开源的内存泄漏探测器,具体使用参考这篇文章
  3. Android Monitor:Android Studio 自带的分析工具,今天的重头戏

Android Monitor

1.生成HPROF文件,点击红框中有绿色下载箭头的按钮(Dump java Heap),这个会生成以hprof结尾的文件

Android Monitor.png

2.生成的效果如下图,简单介绍一下界面功能

HPROF.png
功能描述:

名称 描述
Total Count 该类的实例总数
Heap Count 所选择的堆中该类的实例的数量
Sizeof 单个实例所占空间大小(如果每个实例所占空间大小不一样则显示0)
Shallow Size 堆里所有实例大小总和(Heap Count * Sizeof)
Retained Size 该类所有实例所支配的内存大小
Instance 具体的实例
Reference Tree 所选实例的引用,以及指向该引用的引用。
Depth GC根节点到所选实例的最短路径的深度
Shallow Size 所选实例的大小
Dominating Size 所选实例所支配的内存大小

3.也许你看完这些也还是一头雾水,不知道怎么分析,没关系,我们不就是要找到内存泄漏的源头嘛,来看这里,要开始放大招啦,看到上图中最右边的 Analyzer Tasks,点击这个版块,这个版块就是主要的查找内存泄漏版块

Analyzer Tasks.png

4.启动页很简单,点击绿色运行按钮就好,之后会在下面列出可能存在内存泄漏的Activity,如果没显示,说明启动的Activity中没有内存泄漏

5.查看引用树(Reference Tree),都是根据这个查找内存泄漏的具体地方的

6.一般有类似三角形的,就是一直被引用这,导致内存泄漏,找到对应的地方也很简单,右键项目点击Jump to Source就会打开对应泄漏源码的地方啦,然后我们在根据上面说的泄漏原因,及解决办法修正就好啦
Jump to Source.png
Leak Code.png

上面一共有五六处内存泄漏,都是我故意写出来的,目的就是为了练习排查分析内存泄漏,特地放上源码,demo中内存泄漏以及修复代码都有,欢迎下载学习,如果觉得不错,顺手给个star

下载地址:MemoryLeakSamples

MemoryLeakSamples.png

参考文献 :

明白原理,轻松应对 Android 常见内存泄漏

HPROF文件查看和分析工具

Memory Leak Patterns in Android


1.如果本文帮到了您,不妨点一下右下角的 分享到 按钮。
2.除非注明,本博文章均为原创,转载请以链接形式标明本文地址。
3.本博文章只代表博主当时的观点或结论,与博主公司没有任何关系。