对于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. 使用Resource Owner Password Credentials Grant授权发放Token

    对应的应用场景是:为自家的网站开发手机 App(非第三方 App),只需用户在 App 上登录,无需用户对 App 所能访问的数据进行授权. 客户端获取Token: public string Get ...

  2. linux中的开机和关机命令

    与关机.重新启动相关的命令 * 将数据同步写入硬盘中的命令  sync * 惯用的关机命令  shutdown * 重新启动.关机  reboot halt poweroff sync 强制将内存中的 ...

  3. NC WebService接口开发流程

    一.定义类: 接口类 包定义在public下,接口类名为I开头,Service结尾 实现类 包定义在private下,实现类名以ServiceImpl结尾 VO类 若有VO类,也放在public下 U ...

  4. iOS键盘出现时界面跟着往上推

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyBoardWillShow:) name:UI ...

  5. Hibernate缓存之初探

    数据层的访问效率优化可能第一想到的就是利用缓存,缓存的机能可以简单理解为将从数据库中访问的数据放在内存中,在以后再次使用到这些数据时可以直接从内存中读取而不必要再次访问数据库,尽量减少和数据库的交互提 ...

  6. webstorm与SAE的svn仓库链接进行版本控制

    这里把我使用webstorm与SAE的svn仓库链接: 1.先得设置webstorm中的版本控制,File->Settings->Version Control->Subversio ...

  7. UML用例图中泛化、扩展、包括

    在画用例图的时候,理清用例之间的关系是重点.用例的关系有泛化(generalization).扩展(extend)和包含(include).其中include和extend最易混淆.下面我们结合实例彻 ...

  8. 【转】Linux下打包压缩war和解压war包

    解压demo.war $ mkdir demo //解压前建议先创建目录,否则直接解压到当前目录了 $ cd demo/ $ jar -xvf ../demo.war 解压到指定的demo目录,然后就 ...

  9. Acunetix Web漏洞扫描器

    1.主要程序介绍 主要操作区域简介: b).工具栏 从左到右分别是(这些都可以在主要操作区域找到,所以不常用): 新建扫描——网站扫描——网站爬行——目标查找——目标探测——子域名扫描——SQL盲注— ...

  10. Memcached监听多个端口_同一台Windows机器中启动多个Memcached服务

    下载Memcached服务器 假设你解压在"C:\Program Files\memcached\memcached.exe" 那么可以如下创建多个服务监听不同的端口啦 监听第一个 ...