对于Android这种手持设备来说,通常不会带有太大的内存,而且一般用户都是长时间不重启手机,所以编写程序的时候必须要非常小心的使用内存,尽量避免有内存泄露的问题出现。通常分析程序中潜在内存泄露的问题是一件很有难度的工作,一般都是由团队中的资深工程师负责,而且随着程序代码量的提高,难度还会逐步加大。

今天要介绍一个在Eclipse中使用的内存分析工具——MAT(Eclipse Memory Analyzer,主页在http://www.eclipse.org/mat/)。它是一个功能非常丰富的Java堆转储文件分析工具,而且简单易用,只需点几下下鼠标就可以生成一个专业的分析报告。更重要的是,它和DDMS能够无缝集成,可以帮助你发现Android程序中潜在的内存泄露问题,同时可以协助你优化程序,更加高效的使用内存。

一、安装 MAT

MAT是Eclipse中的一个插件,所以安装非常简单。

首先,通过 Help -> Install Software... 启动软件安装页面:

选择Add…,会弹出一个对话框,名字(Name)中填MAT(其实随便填什么),位置(Location)中填http://download.eclipse.org/mat/1.4/update-site/,然后点OK:

接下来选择你想要安装的 MAT 的功能。因为只需要在Eclipse中使用MAT,所以这里只需要选择Memory Analyzer for Eclipse IDE。特别提一下,Memory Analyzer (Chart) 这个功能是一个可选的安装项,主要用来将数据生成相关的可视化报表,通常这样更加直观,更容易发现问题。

剩下的就一路点Next就好了。

二、几个基本概念

要想看懂MAT生成的报告,还需要掌握几个基本的概念。

Java内存回收

Java程序中,内存空间中垃圾回收的工作是由垃圾回收器(Garbage Collector,GC)完成的。它的核心思想是,对虚拟机中堆内存空间中的对象进行识别,如果对象正在被别的对象引用,那么称其为存活对象;反之,如果对象不再被任何别的对象所引用,则为垃圾对象,可以回收其占据的空间,用于再分配。

对于Android程序来说,其Dalvik虚拟机本身就是一个类Java的虚拟机实现,也具有主动垃圾回收功能。

GC根对象(GC Root)

在垃圾回收机制中,有一组特殊的对象被称为根对象,它们是一组被虚拟机直接引用的对象。比如,正在运行的线程对象,系统调用栈里面的对象,以及被系统类加载器所加载进来的对象。堆空间中的每个对象都是由一个根对象为起点被层层引用的,只有它们引用别的对象,没有其它任何对象可以引用它们。因此,如果一个对象还被某一个存活的根元素所直接或间接的引用,就会被认为是存活对象,不能被回收,进行内存释放。

Shallow Size和Retained Size

1)Shallow Size(表面大小)是指对象本身所占用的内存空间大小,不包含被其引用到的对象所占的内存空间。一个对象的Shallow Size取决于这个对象的实例变量的类型和个数。一个数组对象的Shallow Size是数组中保存的对象的Shallow Size乘以数组元素的个数。一个集合对象的Shallow Size是集合内所有对象的Shallow Size之和。

2)Retained Size(保留大小)是指对象本身的Shallow Size加上从其本身开始所能直接或间接访问到的,且只能由其开始才能访问到的所有对象的Shallow Size之和。这个大小就表明,如果将这个对象释放之后,垃圾回收器所能回收的所有内存大小。

下面举个例子说明,假设对象的引用关系如下:

第一张图中计算对象obj1的Retained Size要包含obj1、obj2和obj4,因为obj3和obj5还可以被根对象引用到,所以不能被包含进来。而在第二张图中,obj3只能被obj1间接引用到,所以要包含进来。

支配树(Dominator Tree)

支配树的定义是,如果在对象引用关系图中,从任意一个对象,如果想访问对象Y的话,必须通过对象X,那么对象X就处于对象Y的支配(Dominate)地位。

可以看出来,支配树的概念其实和前面的Retained Size是相关联的。某个对象的Retained Size就是从这个对象开始,以及从它开始所有支配树子节点的Shallow Size之和。

而且,所有根节点一定是支配树中的顶层节点,反之不一定。

堆转储文件(Heap Dump)

所谓堆转储文件,是在一个特定时间点,对Java进程的内存快照文件。它包含了快照被触发时,Java对象和类在堆中的使用情况。由于快照只是一瞬间的事情,所以只能包含在这个时间点各个对象之间的引用关系,而无法知道一个对象在什么时间点,由哪个方法创建等信息。

MAT其实就是一个通用的Java堆转储文件的分析工具,而通过Android自带的DDMS,可以获得指定设备上指定进程的堆转储文件,将两者结合起来刚好可以达到分析Android程序内存使用状况的目的。

三、如何分析

首先,将你想要分析的程序启动起来,并确保其是可调试的(即在AndroidManifest.xml文件中申明android:debuggable=”true”)。

然后,用DDMS收集Android设备上,你想调试程序的堆转储文件。这步也很简单,在DDMS中,选取你的程序,并点击Dump HPROF file:

DDMS生成转储文件可能要花一点时间,请耐心等待一会。当转储文件生成好了之后,Eclipse会自动调用MAT进行分析,并生成报告:

在这份报告中,我们可以获得如下信息:

1)Histogram:列出了当前程序中每个类被实例化出了多少个对象;

2)Dominator Tree:列出了当前程序中各个活动对象间的支配树关系;

3)TopConsumer:列出了当前程序中Retained Size最大的前几个对象等信息;

4)DuplicateClasses:列出了当前程序中是否有相同的类被不同的类加载器加载的情况。

Histogram和Dominator Tree的区别是分析问题的角度不一样,Histogram是站在类的角度上去分析,而Dominator Tree是站的对象实例的角度上去分析,Dominator Tree可以更方便的看出各个对象间的引用关系。

有了这些武器之后,分析内存泄露问题就变得得心应手了,一般的流程如下:

1)在Histogram和Dominator Tree视图中,根据Retained Heap排序,找出排名靠前的几个类和对象。这些都是后面要重点进行分析的对象。

2)每隔一定时间段生成一次MAT分析报告,对比这几次报告,在在Histogram和Dominator Tree视图中找出Retailed Heap不断增大的几个类和对象。持续占用内存,还不释放,通常意味着有潜在的内存泄露问题。

3)当然,如果一个对象的Retained Size很大,并不一定代表它一定是有问题的,还需要具体情况具体分析。具体来说,我们可以分析一个对象到根对象的引用路径来分析为什么该对象不能被顺利回收。如果说一个对象已经不被任何程序逻辑所需要,但是还存在被根对象引用的情况,基本上就可以说这里存在内存泄露的问题。通过Histogram或者Dominator Tree视图找到疑似有问题的类或者对象之后,选择Path to GC Roots或者Merge Shortest Paths to GC Roots。这里有很多过滤选项,一般来讲可以选择exclude all phantom/weak/soft etc.references。这样就排除了虚引用、弱引用、以及软引用,剩下的就是强引用。除了强引用外,其它任何类型的引用,在Java虚拟机需要的情况下,都可以被GC释放掉。如果一个对象始终无法被释放,肯定是因为有强引用存在。

4)接下来就需要直接定位具体的代码,看看如何释放这些不该存在的对象。找到原因,清理干净后,再对照之前的操作,看看对象是否任然持续增长。如果不再,则说明这个潜在的内存泄露问题已经被修复了。

在Eclipse中使用MAT分析Android程序内存使用状况(转)的更多相关文章

  1. 你不知道的Eclipse的用法:使用MAT分析Android的内存

    如果使用DDMS确实发现了我们程序中存在内存泄露,那如何定位到具体出现问题的代码片段,最终找到问题所在呢?如果从头到尾分析代码逻辑,那肯定会把人逼疯,特别是在维护别人写的代码的时候.这里介绍一个极好的 ...

  2. 如何用MAT分析Android程序的内存泄露

    本文结合<Android开发艺术探索>书籍中的内存分析例子来讲解如何利用MAT工具来查找内存泄漏(以AndroidStudio开发工具为例). 1.下载MAT(Eclipse Memory ...

  3. 如何用MAT分析Android应用内存泄露

    使用工具:Android Studio 2.0 Preview, Android Device Monitor, MAT(Memory Analyzer). 点击Android Studio工具栏上的 ...

  4. 老李案例分享:MAT分析应用程序服务出现内存溢出过程

    老李案例分享:MAT分析应用程序服务出现内存溢出过程   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.在poptest的loa ...

  5. Chapter2——如何分析Android程序

    前几天买了<Android软件安全与逆向分析>这本书,决定在这里记一些笔记. 第一章介绍了如何搭建环境,此处略去:第二章开始讲分析Android程序. 下面按顺序记录关键内容. ----- ...

  6. eclipse中如何查看一个android模拟器的内部文件

    eclipse中如何查看一个android模拟器的内部文件,有时要在其中添加一个文件夹或是什么的,要手动的做这件事,而不能够用代码去完成时,就要用这个方法了. 1.首先,打开一个安卓模拟器. 2.这个 ...

  7. 在Eclipse中使用WindowBuilder设计Swing程序

    在Eclipse中使用WindowBuilder设计Swing程序     Swing程序表示Java的客户端窗体程序,除了通过手动编写代码的方式设计Swing程序之外,Eclipse中还提供了一种W ...

  8. 利用Android Studio、MAT对Android进行内存泄漏检测

    利用Android Studio.MAT对Android进行内存泄漏检测 Android开发中难免会遇到各种内存泄漏,如果不及时发现处理,会导致出现内存越用越大,可能会因为内存泄漏导致出现各种奇怪的c ...

  9. Net Memory Profiler 分析.Net程序内存泄露

    Net Memory Profiler 分析.Net程序内存泄露 Haozes's Tech Space 人類的全部才能無非是時間和耐心的混合物 使用.Net Memory Profiler 分析.N ...

随机推荐

  1. Jungle Roads

    Description The Head Elder of the tropical island of Lagrishan has a problem. A burst of foreign aid ...

  2. Lucene.Net+盘古分词器(详细介绍)(转)

    出处:http://www.cnblogs.com/magicchaiy/archive/2013/06/07/LuceneNet%E7%9B%98%E5%8F%A4%E5%88%86%E8%AF%8 ...

  3. C# Delegate 匿名 Delegate

    C#6.0新添加了 lambda的强力支持,用lambda的确可以节省好多代码,让代码看起来更简洁,更直观: 这里做一个笔记,C#的匿名委托 Demo class Program { static v ...

  4. Javascript数组方法(译)

    在JavaScript中,数组可以使用Array构造函数来创建,或使用[]快速创建,这也是首选的方法.数组是继承自Object的原型,并且他对typeof没有特殊的返回值,他只返回'object'. ...

  5. BZOJ4293 [PA2015]Siano(线段树)

    传送门 这Seg确实不好写,不过因为它与ai的相对顺序无关,所以,我们在对ai排序之后,就可做了.维护一个区间最大值,维护一个和,维护一个区间赋值的懒标记,再维护一个时间变化的标记就可以了. 因为不论 ...

  6. 1.3 Content Provider

    ContentProvider向我们提供了我们在应用程序之间共享数据的一种机制,分为系统的和自定义的,系统的也就是例如联系人,图片等数据. 使用方式:一个应用实现ContentProvider来提供内 ...

  7. URAL - 1920 Titan Ruins: the Infinite Power of Magic(乱搞)

    搞死人的题目,,, 就是在n*n的方格中找路径长度为L的回路. 开始的思路值适合n为偶数的情况,而忽视了奇数的case,所以wa了一次. 然后找奇数case的策略,代码从70多行变成了100多,然后改 ...

  8. redhat 6上nis配置

    redhat 6上nis有点改动.在这里记一下 新装系统以后,首先要 yum install ypserv 安装ypserv的包 然后是设置 # 设置nis服务器名字 ypdomainname cen ...

  9. linux-5重要进程守护

    当给一台主机安装上linux系统后可以工作了-包括接受用户的输入/计算/存储/再将结果输出等等,这都是系统服务帮助我们完成的.而有一些系统服务时刻等待用户的输入(r如键盘进程)或随时响应用户的请求(如 ...

  10. 【原创】注册assembly 到GAC

    http://msdn.microsoft.com/en-us/library/dkkx7f79.aspx http://stackoverflow.com/questions/2182316/how ...