当前位置: 首页 > 后端技术 > Java

使用EclipseMemoryAnalyzer分析dump文件

时间:2023-04-01 21:47:00 Java

然后通过上面学习的GC日志和DDMS工具,我们现在可以很容易的找出应用程序是否存在内存泄漏。但是如果出现了内存泄漏,我们应该如何定位具体的问题呢?这需要借助名为EclipseMemoryAnalyzer(MAT)的内存分析工具。我们需要先下载这个工具,下载地址是:http://eclipse.org/mat/downlo....这个工具分为Eclipse插件版和独立版。如果使用Eclipse开发,可以使用插件版的MAT,非常方便。如果您使用AndroidStudio进行开发,则只能使用独立版本的MAT。下载完成后,我们开始学习如何分析内存泄漏的原因。首先进入DDMS界面,然后在左侧面板中选择我们要观察的应用进程,然后点击DumpHPROFfile按钮,如下图:点击这个按钮后,需要稍等片刻,然后会生成一个HPROF文件,里面记录了我们应用程序内部的所有数据。但是目前MAT还是打不开这个文件,我们还需要把这个HPROF文件从Dalvik格式转换成J2SE格式,使用hprof-conv命令完成转换工作,如下图:hprof-convdump.hprofconverted-倾倒。hprofhprof-conv命令文件存放在/platform-tools目录下。另外,如果你使用的是插件版的MAT,也可以直接在Eclipse中打开生成的HPROF文件,无需经过格式转换这一步。好了,那我们可以尝试使用MAT工具来分析内存泄漏的原因。这里需要提醒的是,MAT并不会告诉我们内存泄漏具体发生在什么地方,而是会提供很多数据和线索,我们需要自己去分析这些数据来判断是否真的发生了内存泄漏。然后现在运行MAT工具,然后选择打开转换后的converted-dump.hprof文件,如下图:MAT提供了很多功能,这里我们只需要学习最常用的几个。上图中间的饼图显示了最大对象占用内存的比例。这张图提供的内容不多,我们可以忽略。这个饼图下面有几个非常好用的工具,一起来了解一下吧。直方图可以列出内存中每个对象的名称、数量和大小。DominatorTree将内存中的所有对象按大小排序,我们可以分析对象之间的引用结构。一般上面两个函数是最常用的,那我们先从DominatorTree说起。现在点击DominatorTree,结果如下图所示:这张图信息量很大,我带大家一起分析一下。首先,RetainedHeap表示这个对象和它持有的其他引用(包括直接和间接)占用的内存总量。所以从上图来看,前两行的RetainedHeap是最大的。我们在分析内存泄漏的时候,内存最大的对象也是最可疑的。此外,您应该能够注意到每一行的最左侧都有一个文件类型图标。其中一些图标的左下角有一个红点,而另一些则没有。带红点的对象表示可以被GCRoots访问。根据上面的解释,GCRoots可以访问到的对象是不能回收的。那么这是否意味着所有带红色的物体都是泄漏物体?当然不是,因为有些对象系统需要一直使用,不应该被回收。我们可以注意到,上图中所有带红点的对象,最右边都写了一个SystemClass,说明这是系统管理的对象,不是我们自己创建的对象,造成内存泄漏。那么上图中你看不出内存泄漏的原因吗?确实,内存泄漏并不是那么容易发现的,需要我们进一步分析。上图中,除了SystemClass这一行,最大的就是第二行的Bitmap对象。虽然Bitmap对象现在不能被GCRoots访问,但不代表Bitmap持有的其他引用不会被GCRoots访问。现在我们可以在第二行右击->PathtoGCRoots->excludeweakreferences,为什么要选择excludeweakreferences呢?因为弱引用不会阻止对象被垃圾回收器回收,所以我们这里直接排除,结果如下图所示:可以看到,Bitmap对象被逐层引用后,到达MainActivity$LeakClass的对象,然后图标的左下角有一个红色的图标,代表可以被GCRoots访问到,而且这个是我们自己创建的Thread,不是SystemClass,所以MainActivity$LeakClass可以被GCRoots访问。它不能被回收,它持有的其他引用也不能被回收,包括MainActivity和MainActivity中包含的图片。这样,我们就成功的找出了内存泄漏的原因。这是DominatorTree中常用的分析方法,即搜索内存大的对象到GCRoots的路径,因为内存占用高的对象更可疑。接下来,让我们再次学习如何使用直方图。回到Overview界面,点击Histogram。结果如下图所示:这里列出了当前应用中所有对象的名称、数量和大小。需要注意的是,这里的对象只有ShallowHeap,没有RetainedHeap,那么ShallowHeap是什么意思呢?是当前对象自身占用内存的大小,不包括引用关系。比如上图中,byte[]对象的ShallowHeap是最高的,说明我们的应用使用了很多byte[]类型的数据,比如图片。你可以右击->Listobjects->withincomingreferences查看谁在使用这些byte[]。那么如何通过Histogram来分析内存泄漏的原因呢?当然也可以使用类似DominatorTree的方法,即分析内存大的对象。比如上图中的byte[]对象,内存占用就很高。通过分析byte[],我们终于可以找到内存泄漏的位置。但是这里我打算用另一种更适合Histogram的方式。如您所见,可以在直方图中显示对象的数量。比如我们怀疑MainActivity可能存在内存泄漏,我们可以在第一行的正则表达式框中搜索“MainActivity”,如下图:可以看出所有包含“MainActivity”字样的对象列在这里,第一行是MainActivity的实例。但是大家有没有发现,当前内存中有11个MainActivity的实例,太不正常了。一般情况下,一个Activity应该只有一个实例。其实这些对象都是刚才不断的横竖屏切换产生的,因为一旦横竖屏切换,Activity会经历一个重新创建的过程,但是由于LeakClass的存在,之前的Activity不能被系统回收,所以会出现这种情况,就是一个Activity有多个实例。接下来右击MainActivity->Listobjects->withincomingreferences查看具体的MainActivity实例,如下图:MainActivity->PathtoGCRoots->excludeweakreferences,结果如下图所示:可以看到,我们又找到了内存泄漏的原因,是MainActivity$LeakClass对象引起的。嗯,这些可能是MAT工具最常用的一些用法。当然要提醒大家,工具是死的,人是活的。MAT没有办法保证可以找出内存泄漏的原因。我们对程序的代码有了足够的了解,知道哪些物体是活着的,以及它们存活的原因,再结合MAT给出的数据进行具体分析,这样就有可能隐藏一些隐藏的物体。找出问题的原因。