Android内存泄露的分析方法

前言

内存泄露,这个耳熟能详的一个bug,每次我在项目中遇到这种问题都非常的头疼,不像普通的异常奔溃比如JE什么的,只要通过Log中的异常堆栈就可以轻松的定位出问题点。那么到底什么是内存泄漏呢?简单粗俗的讲,就是该被释放的对象没有释放,一直被某个或某些实例所持有却不再被使用导致 GC 不能回收。导致这种情况原因是生命周期长的对象持有了生命周期短的对象导致的生命周期短的对象无法被回收。要想定位出内存泄露问题,我们可以通过目前主流的MAT,Android Profiler,LeakCanary等工具来定位问题。

一、MAT简介

MAT是专门用于分析内存的工具,在Eclipse中只要安装相应的插件即可。用于解析反应当前设备内存映像的hprof文件,可以很直观的开出当前抓到的内存情况。一般该文件包含如下内容:

  • 所有的对象信息,包括对象实例、成员变量、存储于栈中的基本类型值和存储于堆中的其他对象的引用值。
  • 所有的类信息,包括classloader、类名称、父类、静态变量等
  • GCRoot到所有的这些对象的引用路径
  • 线程信息,包括线程的调用栈及此线程的线程局部变量(TLS)

二、Android Profiler简介

Android Studio 3.0 采用全新的Android Profiler窗口取代 Android Monitor 工具。 这些全新的分析工具能够提供关于应用 CPU、内存和网络 Activity 的实时数据。 您可以执行基于样本的函数跟踪来记录代码执行时间、采集堆转储数据、查看内存分配,以及查看网络传输文件的详情。

三、如何抓取hprof文件

hprof文件有三种方式来获取:1、DDMS抓取。2、adb命令行抓取,3、Android Studio抓取。有时抓取到的hprof格式并不是标准的可以使用如下命令转换

hprof-conv <source path + source + filename> <out path + out + filename>

1、DDMS抓取:

image

如上图所示可以快速抓取到SystmeUI当前内存情况的hprof文件。

2、adb命令行抓取(如果没有权限开启可以尝试adb shell setprop ro.debuggable 1方式来开启io权限):

1、adb shell am dumpheap <pid/processname> <out path + filename>(必须要指定out目录)
2、adb shell pull <path + xx.hprof> <out path>
通过以上两个命令即可抓取到hprof文件

3、Android Studio抓取(基于Android 3.0以上):

首先打开Android Profier窗口我们可以看到如下的内容:

image

第一部分用于展示CPU的使用状态,第二部分用于显示内存使用情况,第三部分展示网络状态。今天我们的重点是第二部分的内存(MEMORY)。点击MEMORY进入专门的内存展示界面,如图:
image

该界面可以实时的展示当前进程内存的使用情况,包含了JAVA,NATIVE等内存。并通过不同的颜色来区分,可以非常方便的查看分析问题。如果知道内存泄露的必现路径,可以在该界面清晰的看到内存的增长。
然后通过如下方法就可以dump出hprof文件:
image

通过Android Profier生成的hprof文件也不是标准的,AS提供了便捷的转换方式:AS提供了便捷的转换方式:Memory Monitor生成的hpof文件都会显示在AS左侧的Captures标签中,在Captures标签中选择要转换的hpof文件,并点击鼠标右键,在弹出的菜单中选择Export to standard.hprof选项,即可导出标准的hpof文件,如下图所示。
image

Tips : 如果有的同学找不到Android Profiler窗口可以通过如下方式打开:View > Tool windows > Android Profile

四、如何通过MAT分析谁泄露(本文主要讲常用的分析方法)

通过MAT打开hprof文件,选择Leak Suspects Report选项,就会生产分析报告。主要分为两个部分:1、Overview。2、Leak Suspects(泄漏猜想)。如下如所示:

image

该分析报告可以基本看到内存的使用情况,以及一些泄漏猜想(一般定位不出问题来)。主要是通过查看Histogram 或者 Dominator Tree来分析问题。

Histogram

首先我们先说说Histogram。Histogram主要通过类的角度分析,注重的是量的分析。通过点击工具栏上

image
按钮选择柱状图方式分析。内容如图:
image

主要有四个方面的内容。1、Class Name。2、Objects(该类的个数)。3、ShallowHeap(该类本身所占用的大小)。4、Retained Heap。
Retained Heap:一个对象的Retained Set所包含对象所占内存的总大小。换句话说,Retained Heap就是当前对象被GC后,从Heap上总共能释放掉的内存。(Retained Set指的是这个对象本身和他持有引用的对象以及这些引用对象的Retained Set所占内存大小的总和)官方的图解如下所示:
image

从图中可以看出E的Retained Set为E和G。C的Retained Set为C、D、E、F、G、H。
MAT所定义的支配树就是从上图的引用树演化而来。在引用树当中,一条到Y的路径必然会经过X,这就是X支配Y。X直接支配Y则指的是在所有支配Y的对象中,X是Y最近的一个对象。支配树就是反映的这种直接支配关系,在支配树中,父节点直接支配子节点。下图就是官方提供的一个从引用树到支配树的转换示意图。
image

C直接支配D、E,因此C是D、E的父节点,这一点根据上面的阐述很容易得出结论。C直接支配H,这可能会有些疑问,能到达H的主要有两条路径,而这两条路径FD和GE都不是必须要经过的节点,只有C满足了这一点,因此C直接支配H,C就是H的父节点。通过支配树,我们就可以很容易的分析一个对象的Retained Set,比如E被回收,则会释放E、G的内存,而不会释放H的内存,因为F可能还引用着H,只有C被回收,H的内存才会被释放。
假如说我们要找出Activity的情况,我们可以在Regex中输入XXActivity过滤条件来搜索。Regex支持正则表达式
image

RecentActivity以及其他各种内部类对象(Activity后面的美元符号代表的是内部类)有3个,可以断定Activity出现了泄漏。具体查看为啥导致了内存泄漏,可以通过右击选择Merge Shortest Paths to GC Roots选择最小GC的路径,然后选择exclude all phantom/weak/soft etc.references去除一些软,弱引用等对象,找到真正的强引用对象。如下图:
image

选择之后,得出一份如下分析结果:
image

明显可以得出是由于$7的这个匿名内部类持有了Activity对象,导致出现了内存泄漏。刚好遇到的是匿名内部类,无法得知该内部类到底是谁。这时我们只能通过代码查找来查看哪个内部类出现了问题。

Dominator Tree

Dorminator Tree意味支配树,从名称就可以看出Dorminator Tree更善于去分析对象的引用关系。注重的是引用关系。

image

同样我们通过在Regex过滤出XXActivity对象
image

可以发现有好几个RecentsActivity的对象,同样可以断定是RecentsActivity出现了泄露。这边有三种方式分析谁泄露:1、发现RecentsActivity7出现了泄露。2、通过Path to GC Roots的找出GC roots路径方式来如图:
image

3、通过Merge Shortest Paths to GC Roots的方式,该方式上面已经分析过了就不继续展开了。
以上通过两种方法查找出了内存泄露的内容。

五、如何通过Android Profiler分析内存泄露

Memory Profiler 是 Android Profiler 中的一个组件,可帮助您识别导致应用卡顿、冻结甚至崩溃的内存泄漏和流失。 它显示一个应用内存使用量的实时图表,让您可以捕获堆转储、强制执行垃圾回收以及跟踪内存分配。

下面我们通过一个例子来总结内存泄露问题的分析过程:

  1. 首先我们操作内存泄露的必现路径,通过上面介绍的AS抓hprof的方式来抓取泄露hprof文件。
  2. 然后就会直接显示出当前的JAVA对象以及引用的情况。我们可以选择左上角Arrayge by class下拉弹窗来显示app heap的排列方式:
  • 通过Class来排序:基于类名称对所有分配进行分组。
  • 通过Package来排序:基于软件包名称对所有分配进行分组。
  • 通过CallbackBack:将所有分配分组到其对应的调用堆栈。
    image
  1. 然后选择左上角的向上图标导出hprof文件:
    image

    然后重新将hprof文件通过AS打开,选择右上角的Analyzer Tasks
    image

    在点击那个播放按钮可以自动分析出Activity与String泄露的内容。然后选择其中一个Activity就可以看出该Activity被谁给持有
    image

    发现同样也是RecentsActivity7持有,打开多个Activity对象发现都是这个RecentsAcitivity7对象持有导致的泄露。可以注意看持有Activity那个对象会是蓝色显示的,且前面有也有类似于MAT小黄点估计与MAT的小黄点是一个意思。

六、参考链接

  1. Android内存优化(五)详解内存分析工具MAT
  2. Android内存优化(三)避免可控的内存泄漏
  3. Android 内存泄漏总结
  4. 使用MAT工具分析性能(有些左下角有小黄点,带有这个小黄点的就表示可以被GC ROOT访问到,可以被GC Root访问到的对象是不可以被回收的,当然不代表带有小黄点的就是内存泄漏的,也有些是一直被系统所使用的。其实也不算内存泄漏因为在这三个后面都是显示的System Class,代表着它被系统所占用)
  5. Android性能优化:彻底解决内存泄漏
  6. HPROF Viewer and Analyzer
  7. 使用 Memory Profiler 查看 Java 堆和内存分配
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容

  • 前言 内存泄露,这个耳熟能详的一个bug,每次我在项目中遇到这种问题都非常的头疼,不像普通的异常奔溃比如JE什么的...
    不正就是歪573阅读 1,428评论 0 51
  • 本文主要有以下三部分内容:第一部分:简单介绍开发者指南上内存相关的文章。第二部分:总结移动App性能评测与优化内存...
    htkeepmoving阅读 3,987评论 0 5
  • 一、 Android的内存机制 Android的程序由Java语言编写,所以Android的内存管理与Java的内...
    陈大冲阅读 1,098评论 0 4
  • 1、内存了解 在Android App的性能优化的各个部分里,内存方面的知识较多且不易理解,内存的问题绝对是最令人...
    萧竹阅读 8,699评论 1 12
  • 一、Android内存分析基础 1、查看进程分配的最大内存 每个App进程可以分配到的最大内存是有限的,当然不同型...
    砺雪凝霜阅读 2,079评论 0 4