Android 应用内存泄漏分析(实战篇)

 在Android应用开发中,内存泄漏比较常见;如果应用越来越卡顿以致被系统杀掉,这个时候就可靠考虑分析下是否是内存泄漏导致的。在此分享内存泄漏分析的一种方式,主要是通过ADB、MAT工具分析,并简单描述内存泄漏的原因。

工具使用

 分析应用内存,首先查看应用内存使用情况,可以通过Android Studio Profiler 或者dumpsys meminfo 命令查看,然后再通过内存分析工具分析内存泄漏情况。Android 内存泄漏分析工具主要有两种:1,通过Android Stuido Profiler工具实时查看内存使用情况;2,获取应用的hprof文件,然后通过MAT工具分析。我们今天要介绍的就是第二种方式;

查看内存使用情况

 Android Studio Profiler查看内存比较直观,但这次我们主要看命令的方式,因为Android Studio Profiler无法应用于Android5.0以下的机器。比如查看A应用的内存使用情况,使用"dumpsys meminfo A应用包名"
Android 应用内存泄漏分析(实战篇)_第1张图片

  • 私有内存(Dirty and Clean):
    进程独占内存,也就是进程销毁时可以回收的内存容量。通常private Dirty内存是最重要的部分,因为只被自己进程使用,这也是我们需要关注的。Dirty内存是已经被修改的内存页,因此必须常驻内存(因为没有swap);Clean内存是已经映射持久文件使用的内存页(例如正在被执行的代码),因此一段时间不使用的话就可以置换出去。

  • 实际使用内存(PSS):
    将跨进程共享页也加入进来, 进行按比例计算PSS。这样能够比较准确的表示进程占用的实际物理内存。

当我们操作应用的时候如果发现TOTAl Private Dirty内存只增无减,那就很有可能存在内存泄漏,需要将应用的堆内存dump出来。

获取hprof文件

 获取hprof文件主要包含两个步骤:

  • dump 出java heap 文件,通过Android Studio Profiler 工具或者“am dumpheap”命令导出java heap文件

    root@M1AEV-MY2:/ # am dumpheap 应用包名 /sdcard/xxx.hprof
    
  • 将dump出的java heap 文件通过hprof-conv 转化成MAT工具可识别的hprof文件;

    hprof-conv E:\zhuzp\log\xxx.hprof E:\zhuzp\log\memoryleak\xxx.hprof
    

    通过上面两个步骤就得到了MAT可识别的hprof文件,接下来就是开始通过MAT工具分析

MAT工具

 MAT(Memory Analyzer Tool)是一款快速且功能丰富的Java堆内存分析工具,它能帮助你发现内存泄漏问题以减少内存消耗。

 通过MAT工具打开一个hprof文件,进入概述图界面显示如下:
Android 应用内存泄漏分析(实战篇)_第2张图片

OverView 界面有以下几类:

  • Details

    Size: 66 MB Classes: 4.7k Objects: 806.1k Class Loader: 5 Unreachable Objects Histogram
    

    Java heap 内存总共 66M,有4700个类,806100个对象,5个ClassLoader,前面的这些内容都很明了。唯一需要注意的是Ureachable Objects Histogram,这是显示GC不可达对象,也就是会被GC回收的对象柱状图。

  • Biggest Objects by Retained Size

    这一块比较直观的显示出内存占用最大的对象,通过将鼠标在饼图上移动可以看到各个对象,比如将鼠标放在最大块上时显示出的就是RadioDataObservable
    Android 应用内存泄漏分析(实战篇)_第3张图片

  • Actions

    Actions下有几个比较重要的内容:Histogram,显示每个类对象的个数;Dominator Tree 支配节点树,显示最大的对象和它所keep alive 的对象。

  • Reports

    Reports 下面的两个内容也比较重要:Leak Suspect 可能存在内存泄漏的地方;Top Component 内存占用超过总数1%的对象;

  • Step by Step 这可以理解成基本使用教程

 在了解基本界面后,看几个比较重要且常用的功能界面;

Leak Suspect

  当打开hprof文件时默认是进入该界面,该界面会显示可能存在内存泄漏的地方。如果分析内存泄漏,主要也是从这个界面开始分析。比如下面图中就显示了两处可能存在内存泄漏的地方:1,RadioDataObservable;2,Bitmap;
Android 应用内存泄漏分析(实战篇)_第4张图片
  通过点击Details进入内存泄漏详细界面,这里只需要关注Accumulated Objects in Dominator Tree,这个可以理解为堆积对象的支配节点树(也就是内存泄漏对象持有的引用关系)。Shortest Path To the Accumulation Point,可以理解为内存泄漏对象被哪些最短引用链对象所引用。
Android 应用内存泄漏分析(实战篇)_第5张图片
点击图片中的标注,进入支配节点树界面,这里列举了Object数据包含了哪些对象,可以看到都是SubRadioFragment对象,并且在最下面显示了对象的总数。也就是说RadioDataObserverable中的ArrayList集合中存在266个SubRadioFragment对象。
Android 应用内存泄漏分析(实战篇)_第6张图片
 到这里基本就能定位内存泄漏的原因了,很有可能就是一直往集合里面添加对象,没有移除对象。通过追溯代码也确实是因为只在Fragment onCreateView()时往DataObservable中添加Observer,在onDestroyView()没有将Observer移除。

 通过MAT工具分析内存泄漏到这一步,其实也基本结束了。不过再了解Histogram、Dominator Tree、Top Consumers对我们应用的内存优化也有一定的帮助。

Histogram

 柱状图显示的是每个类实例的个数,在此界面可以找出哪些类实例占用内存比较大。
Android 应用内存泄漏分析(实战篇)_第7张图片
首先需要明白Retained Heap和Shallow Heap。Shallow Heap是对象本身占据的内存的大小,不包含其引用的对象。Retained Heap是当前对象大小+当前对象可直接或间接引用到的对象的大小总和,并且排除被GC Roots直接引用的对象
Android 应用内存泄漏分析(实战篇)_第8张图片
如上图所示,对象A的Shallow Heap大小等于Retained Heap,因为对象A没有引用其他对象。对象B的Retained heap大小等于对象B、对象D、对象E和对象F(但不包括对象C) Shallow heap总和。

在Histogram界面任意右键一个类,显示如下:Android 应用内存泄漏分析(实战篇)_第9张图片

这个地方关注项内容

  • List objects 显示所有对象,包括两种方式;with outgoing references 是指以对象持有哪些引用的形式显示出来所有对象;with incoming references 是指以对象被引用的关系链形式显示出所以对象;

Android 应用内存泄漏分析(实战篇)_第10张图片
Android 应用内存泄漏分析(实战篇)_第11张图片

  • Merge Shortest Paths to GC Roots 对象到GC Roots 的最短路径

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KdynwjGC-1574424861660)(./memory_shortest_gc_roots.png)]Android 应用内存泄漏分析(实战篇)_第12张图片

    在这可以选择过滤不同的引用类型,常用的就是过滤所有幽灵引用、弱引用、软引用只查看强引用,因为其他引用对象都能够被GC回收。从下图可以看到RadioFragmnet对象的GC Roots 对象就是静态变量sRadioDataObservable;
    Android 应用内存泄漏分析(实战篇)_第13张图片

原理

  上面我们讲的是分析内存泄漏的方式,那到底是什么原因导致内存泄漏的呢。本该被回收的对象,但是由于编码不当导致对象没有被回收。 不同的GC算法在回收对象前都会判断对象是否是活动对象,判断活动对象的方式是判断该对象和“GC Roots对象”是否存在引用链。有关内容可以参考《Android内存泄漏原理及优化(原理篇)》

你可能感兴趣的:(android)