精华内容
下载资源
问答
  • 内存分析工具MAT

    2018-01-09 18:16:19
    Android内存分析工具,和AS的.hprof文件配合使用分析应用APP的内存。
  • 内存分析工具MAT的使用 一MAT插件安装 MAT(Memory Analyzer Tool) 是基于heap dumps来进行分析的它的分析速度比jhat快分析结果是图形界面显示比java内置jhat的可读性更高,通过Eclipse市场安装 方法/步骤1 打开...
  • 内存分析工具 MAT

    1 内存泄漏的排查方法

    Dalvik Debug Monitor Server (DDMS) ADT插件的一部分,其中有两项功能可用于内存检查 :

    ·    heap 查看堆的分配情况

    ·    allocation tracker跟踪内存分配情况

    DDMS 这两项功能有助于找到内存泄漏的操作行为。

    Eclipse Memory Analysis Tools (MAT) 是一个分析 Java堆数据的专业工具,用它可以定位内存泄漏的原因。

    工具地址 : https://www.eclipse.org/mat/

    1.1 观察 Heap

    ·        运行程序,然后进入 DDMS管理界面,如下:

     

    PS : 点击工具栏上的  来更新统计信息

    点击右侧的 Cause GC 按钮或工具栏上的  即可查看当前的堆情况,如下: 


    主要关注两项数据:

    o    Heap Size 堆的大小,当资源增加,当前堆的空余空间不够时,系统会增加堆的大小,若超过上限 (例如 64M,视平台和具体机型而定)则会被杀掉

    o    Allocated 堆中已分配的大小,这是应用程序实际占用的内存大小,资源回收后,此项数据会变小

    ·        查看操作前后的堆数据,看是否有内存泄漏 
    对单一操作(比如添加页,删除页)进行反复操作,如果堆的大小一直增加,则有内存泄漏的隐患。

    1.2 利用MAT分析内存堆

    DDMS 可以将当前的内存 Dump成一个 hprof格式的文件,MAT 读取这个文件后会给出方便阅读的信息,配合它的查找,对比功能,就可以定位内存泄漏的原因。

    ·        获取 hprof文件 
    点击工具栏上的  按钮,将内存信息保存成文件。 如果是用 MAT Eclipse 插件获取的 Dump文件,则不需要经过转换,Adt会自动进行转换然后打开。

    ·        转换 hprof文件 
    DDMS Dump
    出的文件要经过转换才能被 MAT识别,Android SDK提供了这个工具 hprof-conv (位于 sdk/tools)

    ·    ./hprof-conv xxx-a.hprof xxx-b.hprof

    ·         MAT打开转换后的 hprof文件 


    1.3  Histogram 查询

    用的最多的功能是 Histogram,点击 Actions下的 Histogram项将得到 Histogram结果


    它按类名将所有的实例对象列出来,可以点击表头进行排序,在表的第一行可以输入正则表达式来匹配结果 :


    在某一项上右键打开菜单选择 list objects ->with incoming refs 将列出该类的实例:


    它展示了对象间的引用关系,比如展开后的第一个子项表示这个 HomePage(0x420ca5b0) HomePageContainer(0x420c9e40)中的 mHomePage属性所引用.

    快速找出某个实例没被释放的原因,可以右健 Path to GC Roots-->exclue all phantom/weak/soft etc. reference :


    得到的结果是:


    从表中可以看出 PreferenceManager -> … ->HomePage这条线路就引用着这个 HomePage实例。用这个方法可以快速找到某个对象的 GC Root,一个存在 GC Root的对象是不会被 GC回收掉的.

    1.4  Histogram 对比

    为查找内存泄漏,通常需要两个 Dump结果作对比,打开 Navigator History面板,将两个表的 Histogram结果都添加到 Compare Basket中去 :


    添加好后,打开 Compare Basket面板,得到结果:


    点击右上角的 ! 按钮,将得到比对结果:


    注意,上面这个对比结果不利于查找差异,可以调整对比选项


    再把对比的结果排序,就可得到直观的对比结果:


    也可以对比两个对象集合,方法与此类似,都是将两个 Dump结果中的对象集合添加到Compare Basket中去对比。找出差异后用 Histogram查询的方法找出 GC Root,定位到具体的某个对象上。

    1.5  例子

    举例一个典型的分析内存泄漏的过程:

    1.  使用 Heap查看当前堆大小为 23.00M

    2.  添加一个页后堆大小变为 23.40M

    3.  将添加的一个页删除,堆大小为 23.40M

    4.  多次操作,结果仍相似,说明添加/删除页存在内存泄漏 (也应注意排除其它因素的影响)

    5.  Dump 出操作前后的 hprof 文件 (1.hprof,2.hprof),用 mat打开,并得到 histgram结果

    6.  使用 HomePage字段过滤 histgram结果,并列出该类的对象实例列表,看到两个表中的对象集合大小不同,操作后比操作前多出一个 HomePage,说明确实存在泄漏

    7.  将两个列表进行对比,找出多出的一个对象,用查找 GC Root的方法找出是谁串起了这条引用线路,定位结束

    PS :

    ·        很多时候堆增大是 Bitmap引起的,Bitmap Histogram中的类型是 byte [],对比两个 Histogram中的 byte[] 对象就可以找出哪些 Bitmap有差异

    ·        多使用排序功能,对找出差异很有用

    2 内存泄漏的原因分析

    总结出来只有一条: 存在无效的引用! 
    良好的模块设计以及合理使用设计模式有助于解决此问题。

    3 Tips

    ·    使用 android:largeHeap="true"标记 (API Level >= 11) 
    AndroidManifest.xml中的 Application节点中声明即可分配到更大的堆内存, android:largeHeap标记在 Android系统应用中也有广泛的应用 ,比如 Launcher, Browser这些内存大户上均有使用.

    4 参考

    ·    DDMS 官方教程 http://developer.android.com/tools/debugging/ddms.html

    ·    MAT 下载 http://www.eclipse.org/mat/downloads.php

    ·    MAT 使用 http://android-developers.blogspot.tw/2011/03/memory-analysis-for-android.html

    展开全文
  • JVM内存dump分析工具MAT独立安装包,分析内存溢出利器,可以准确定位内存异常原因,解决问题,MemoryAnalyzer-1.10.0.20200225.zip
  • jvm内存分析工具mat

    2019-09-20 23:53:57
    mat用于分析JVM的内存dump信息,是在JVM内存异常时进行内存分析的好工具
  • 在这个系列的前四篇文章中,我分别介绍了DVM、ART、内存泄漏和内存检测工具的相关知识点,这一篇我们通过一个小例子,来学习如何使用内存分析工具MAT

    相关文章
    Android性能优化系列
    Java虚拟机系列

    前言

    在这个系列的前四篇文章中,我分别介绍了DVM、ART、内存泄漏和内存检测工具的相关知识点,这一篇我们通过一个小例子,来学习如何使用内存分析工具MAT。

    1.概述

    在进行内存分析时,我们可以使用Memory Monitor和Heap Dump来观察内存的使用情况、使用Allocation Tracker来跟踪内存分配的情况,也可以通过这些工具来找到疑似发生内存泄漏的位置。但是如果想要深入的进行分析并确定内存泄漏,就要分析
    疑似发生内存泄漏时所生成堆存储文件。堆存储文件可以使用DDMS或者Memory Monitor来生成,输出的文件格式为hpof,而MAT就是来分析堆存储文件的。
    MAT,全称为Memory Analysis Tool,是对内存进行详细分析的工具,它是Eclipse的插件,如果用Android Studio进行开发则需要单独下载它,下载地址为:http://eclipse.org/mat/,这篇文章MAT的版本为1.6.1。

    2.生成hpof文件

    2.1 准备内存泄漏代码

    我们需要准备一段发生内存泄漏代码,如下所示。

    public class MainActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            LeakThread leakThread = new LeakThread();
            leakThread.start();
        }
    
        class LeakThread extends Thread {
            @Override
            public void run() {
                try {
                    Thread.sleep(60 * 60 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    上面的代码是很典型的内存泄漏的例子,原因就是非静态内部类LeakThread持有外部类MainActivity的引用,LeakThread中做了耗时操作,导致MainActivity无法被释放,关于内存泄漏可以查看Android内存优化(三)避免可控的内存泄漏这篇文章。

    2.2 DDMS生成hpof文件

    生成hpof文件主要分为以下几个步骤:
    1. 在Android Studio中打开DDMS,运行程序。
    2. 在Devices中选择要分析的应用程序进程,点击Update Heap按钮(装有一半绿色液体的圆柱体)开始进行追踪。
    3. 进行可能发生内存问题的操作(本文的例子就是不断的切换横竖屏)。
    4. 点击Dump HPROP File按钮结束追踪,生成并保存hprof文件,如下图所示。

    DDMS生成的hprof文件并不是标准的,还需要将它转换为标准的hprof文件,这样才会被MAT识别从而进行分析,可以使用SDK自带的hprof-conv进行转换,它的路径在sdk/platform-tools中,进入到该路径执行以下语句即可:

    hprof-conv D:\before.hprof D:\after.hprof

    其中 D:\before.hprof 是要转换的hprof文件路径,D:\after.hprof 则是转换后hprof文件的保存路径。

    2.3 Memory Monitor生成hpof文件

    除了用DDMS来生成hpof文件,还可以用AS的Memory Monitor来生成hpof文件。
    生成hpof文件主要分为一下几个步骤:
    1. 在Android Monitor中选择要分析的应用程序进程。
    2. 进行可能发生内存问题的操作(本文的例子就是不断的切换横竖屏)。
    3. 点击Dump Java Heap按钮,生成hprof文件,如下图所示。

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

    3.MAT分析hpof文件

    用MAT打开标准的hpof文件,选择Leak Suspects Report选项。这时MAT就会生成报告,这个报告分为两个标签页,一个是Overview,一个是Leak Suspects(内存泄漏猜想),如下图所示。

    Leak Suspects中会给出了MAT认为可能出现内存泄漏问题的地方,上图共给出了3个内存泄漏猜想,通过点击每个内存泄漏猜想的Details可以看到更深入的分析清理情况。如果内存泄漏不是特别的明显,通过Leak Suspects是很难发现内存泄漏的位置。

    打开Overview标签页,首先看到的是一个饼状图,它主要用来显示内存的消耗,饼状图的彩色区域代表被分配的内存,灰色区域的则是空闲内存,点击每个彩色区域可以看到这块区域的详细信息,如下图所示。

    再往下看,Actions一栏的下面列出了MAT提供的四种Action,其中分析内存泄漏最常用的就是Histogram和Dominator Tree。我们点击Actions中给出的链接或者在MAT工具栏中就可以打开Dorminator Tree和Histogram,如下图所示。

    其中左边第二个选项是Histogram,第三个选项是Dorminator Tree,第四个是OQL,下面分别对它们进行介绍。

    3.1 Dominator Tree

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

    图中可以看出Dorminator Tree有三列数据。
    - Shallow Heap:对象自身占用的内存大小,不包括它引用的对象。如果是数组类型的对象,它的大小是数组元素的类型和数组长度决定。如果是非数组类型的对象,它的大小由其成员变量的数量和类型决定。
    - Retained Heap:一个对象的Retained Set所包含对象所占内存的总大小。换句话说,Retained Heap就是当前对象被GC后,从Heap上总共能释放掉的内存。

    Retained Set指的是这个对象本身和他持有引用的对象以及这些引用对象的Retained Set所占内存大小的总和,官方的图解如下所示。

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

    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的内存才会被释放。

    这里对支配树进行了讲解,我们可以得出一个结论:通过MAT提供的Dominator Tree,可以很清晰的得到一个对象的直接支配对象,如果直接支配对象中出现了不该有的对象,就说明发生了内存泄漏。
    在Dominator Tree的顶部Regex可以输入过滤条件(支持正则表达式),如果是查找Activity内存泄漏,可以在Regex中输入Activity的名称,比如我们这个例子可以输入MainActivity,效果如下图所示。


    Dominator Tree中列出了很多MainActivity实例,MainActivity是不该有这么多实例的,基本可以断定发生了内存泄漏,具体内存泄漏的原因,可以查看GC引用链。在MainActivity一项单击鼠标右键,选择Merge Shortest Paths to GC Root,如下图所示。

    Merge Shortest Paths to GC Root选项主要用来显示距离GC Root最短的路径,根据引用类型会有多种选项,比如with all references就是包含所有的引用,这里我们选择exclude all phantom/weak/soft etc. references,因为这个选项排除了虚引用、弱引用和软引用,这些引用一般是可以被回收的。这时MAT就会给出MainActivity的GC引用链。


    引用MainActivity的是LeakThread,this$0的含义就是内部类自动保留的一个指向所在外部类的引用,而这个外部类就是MainActivity,这将会导致MainActivity无法被GC。

    3.2 Histogram

    Histogram与Dominator Tree不同的是,Dominator Tree是在对象实例的角度上进行分析,注重引用关系分析,而Histogram则在类的角度上进行分析,注重量的分析。
    Histogram中的内容如下图所示。

    可以看到Histogram中共用四列数据,关于Shallow Heap和Shallow Heap的含义我们在3.1节已经知道了,剩余的 Class Name代表类名,Objects代表对象实例的个数。

    在Histogram的顶部Regex同样可以输入过滤条件,这里同样输入MainActivity,效果如下图所示。

    MainActivity和LeakThread实例各为11个,基本上可以断定发生了内存泄漏。具体内存泄漏的原因,同样可以查看GC引用链。在MainActivity一项单击鼠标右键,选择Merge Shortest Paths to GC Root,并在选项中选择exclude all phantom/weak/soft etc. references如下图所示。

    得出的结果和3.1节是相同的,引用MainActivity的是LeakThread,这导致了MainActivity无法被GC。

    3.3 OQL

    OQL全称为Object Query Language,类似于SQL语句的查询语言,能够用来查询当前内存中满足指定条件的所有的对象。它的查询语句的基本格式为:

     SELECT * FROM [ INSTANCEOF ]   <class_name> [ WHERE <filter-expression>]

    当我们输入select * from instanceof android.app.Activity并按下F5时(或者按下工具栏的红色叹号),会将当前内存中所有Activity都显示出来,如下图所示。


    如果Activty比较多,或者你想查找具体的类,可以直接输入具体类的完整名称:

    select * from com.example.liuwangshu.leak.MainActivity

    通过查看GC引用链也可以找到内存泄漏的原因。关于OQL语句有很多用法,具体可以查看官方文档

    3.4 对比hpof文件

    因为我们这个例子很简单,可以通过上面的方法来找到内存泄漏的原因,但是复杂的情况就需要通过对比hpof文件来进行分析了。使用步骤为:
    1. 操作应用,生成第一个hpof文件。
    2. 进行一段时间操作,再生成第二个hpof文件。
    3. 用MAT打开这两个hpof文件。
    4. 将第一个和第二个hpof文件的Dominator Tree或者Histogram添加到Compare Basket中,如下图所示。

    5. 在Compare Basket中点击红色叹号按钮生成Compared Tables,Compared Tables如下图所示。

    在Compared Tables也有顶部Regex,输入MainActivity进行筛选。

    可以看到MainActivity在这一过程中增加了6个,MainActivity的实例不应该增加的,这说明发生了内存泄漏,可以通过查看GC引用链来找到内存泄漏的具体的原因。

    除了上面的对比方法,Histogram还可以通过工具栏的对比按钮来进行对比:

    生成的结果和Compared Tables类似,我们输入MainActivity进行筛选:

    可以看到第二个hpof文件比第一个hpof文件多了6个MainActivity实例。

    MAT还有很多功能,这里也只介绍了常用的功能,其他的功能就需要读者在使用过程中去发现并积累。

    参考资料
    《Android群英传 神兵利器》
    《Android应用性能优化最佳实践》
    《高性能Android应用开发》
    利用MAT进行内存泄露分析
    Android最佳性能实践(二)——分析内存的使用情况
    Memory Analyzer


    欢迎关注我的微信公众号,第一时间获得博客更新提醒,以及更多成体系的Android相关原创技术干货。
    扫一扫下方二维码或者长按识别二维码,即可关注。

    展开全文
  • MAT(Memory Analyzer Tools)是一个快速且功能丰富的 Java 堆分析器,可帮助您查找内存泄漏...1 简介MAT 是一款非常强大的内存分析工具,在 Eclipse 中有相应的插件,同时也有单独的安装包。在进行内存分析时,只要获...

    39094ad7a87bca36de075584a3fafa57.gif

    MAT(Memory Analyzer Tools)是一个快速且功能丰富的 Java 堆分析器,可帮助您查找内存泄漏并减少内存消耗。使用 MAT 分析具有数亿个对象的高效堆转储,快速计算对象的保留大小,查看谁阻止垃圾收集器收集对象,运行报告以自动提取泄漏嫌疑者。

    1 简介

    MAT 是一款非常强大的内存分析工具,在 Eclipse 中有相应的插件,同时也有单独的安装包。在进行内存分析时,只要获得了反映当前设备内存映像的 hprof 文件,通过 MAT 打开就可以直观地看到当前的内存信息。

    2 使用

    2.1 准备 MAT

    下载独立版本的 MAT,下载地址:https://www.eclipse.org/mat/downloads.php,下载后解压。找到 MemoryAnalyzer.ini 文件,该文件里面有个 Xmx 参数,该参数表示最大内存占用量,默认为 1024m,根据堆转储文件大小修改该参数即可。

    2.2 准备堆转储文件(Heap Dump)

    堆转储文件(Heap Dump)是 Java 进程在某个时间内的快照(.hprof 格式)。它在触发快照的时候保存了很多信息,如:Java 对象和类信息(通常在写堆转储文件前会触发一次 Full GC)。

    堆转储文件信息:

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

    多种方式获取堆转储文件:

    • 通过 jmap 命令可以在 cmd 里执行:jmap -dump:format=b,file=
    • 如果想在发生内存溢出的时候自动 dump,需要添加下面 JVM 参数:-XX:+HeapDumpOnOutOfMemoryError
    • 使用 Ctrl+Break 组合键主动获取获取,需要添加下面 JVM 参数:-XX:+HeapDumpOnCtrlBreak
    • 使用 HPROF Agent 可以在程序执行结束时或受到 SIGOUT 信号时生成 Dump 文件,配置在虚拟机的参数如下:-agentlib:hprof=heap=dump,format=b
    • 使用 JConsole 获取。
    • 使用 Memory Analyzer Tools 的 File -> Acquire Heap Dump 功能获取。

    2.3 分析堆转储文件

    打开 MAT 之后,加载 dump 文件,差不多就下面这样的界面:

    dd2bacec0eb4a3d622236949aa696afc.png

    常用的两个功能:Histogram、 Leak Suspects

    2.3.1 Histogram

    17422da20b92296fa7328b847a11a5cb.png

    Histogram 可以列出内存中的对象,对象的个数及其内存大小,可以用来定位哪些对象在 Full GC 之后还活着,哪些对象占大部分内存。

    • Class Name:类名称,Java 类名。
    • Objects:类的对象的数量,这个对象被创建了多少个。
    • Shallow Heap:对象本身占用内存的大小,不包含其引用的对象内存,实际分析中作用不大。常规对象(非数组)的 Shallow Size 由其成员变量的数量和类型决定。数组的 Shallow Size 由数组元素的类型(对象类型、基本类型)和数组长度决定。对象成员都是些引用,真正的内存都在堆上,看起来是一堆原生的 byte[], char[], int[],对象本身的内存都很小。
    • Retained Heap:计算方式是将 Retained Set(当该对象被回收时那些将被 GC 回收的对象集合)中的所有对象大小叠加。或者说,因为 X 被释放,导致其它所有被释放对象(包括被递归释放的)所占的 heap 大小。Retained Heap 可以更精确的反映一个对象实际占用的大小。

    Retained Heap 例子:一个 ArrayList 对象持有 100 个对象,每一个占用 16 bytes,如果这个 list 对象被回收,那么其中 100 个对象也可以被回收,可以回收 16*100 + X 的内存,X 代表 ArrayList 的 shallow 大小。


    c761d74a1132a69e4131bf279d6f8cd5.png

    在上述列表中选择一个 Class,右键选择 List objects > with incoming references,在新页面会显示通过这个 class 创建的对象信息。


    65b714a730998ff3abb022f3cf83135d.png

    继续选择一个对象,右键选择 Path to GC Roots > **** ,通常在排查**内存泄漏(一般是因为存在无效的引用)**的时候,我们会选择 exclude all phantom/weak/soft etc.references,意思是查看排除虚引用/弱引用/软引用等的引用链,因为被虚引用/弱引用/软引用的对象可以直接被 GC 给回收,我们要看的就是某个对象否还存在 Strong 引用链(在导出 Heap Dump 之前要手动触发 GC 来保证),如果有,则说明存在内存泄漏,然后再去排查具体引用。

    这时会拿到 GC Roots 到该对象的路径,通过对象之间的引用,可以清楚的看出这个对象没有被回收的原因,然后再去定位问题。如果上面对象此时本来应该是被 GC 掉的,简单的办法就是将其中的某处置为 null 或者 remove 掉,使其到 GC Root 无路径可达,处于不可触及状态,垃圾回收器就可以回收了。反之,一个存在 GC Root 的对象是不会被垃圾回收器回收掉的。

    2.3.2 Leak Suspects

    02c497d82ff3c72e6cd979c52006ff1f.png

    Leak Suspects 可以自动分析并提示可能存在的内存泄漏,可以直接定位到 Class 及对应的行数。


    71ef6b9e68083a409720937d76a34060.png

    比如:这里问题一的描述,列出了一些比较大的实例。点击 Details 可以看到细节信息,另外还可点击 See stacktrace 查看具体的线程栈信息(可直接定位到具体某个类中的方法)。

    ecd3cb238346b7b9bb79ae542b2dda79.png

    在 Details 详情页面 Shortest Paths To the Accumulation Point 表示 GC root 到内存消耗聚集点的最短路径,如果某个内存消耗聚集点有路径到达 GC root,则该内存消耗聚集点不会被当做垃圾被回收。

    实战:在某项目中,其中几个 Tomcat 响应特别慢,打开 Java VisualVM 观察 Tomcat(pid xxx)-Visual GC 发现 Spaces-Old 升高,Graphs-GC Time 比较频繁且持续时间长、有尖峰(重启后过段时间又出现了),最后通过 Leak Suspects 中的 See stacktrace 定位到某个查询接口,仔细排查代码后发现有个 BUG:在特定查询条件下会一次性查询几万的数据出来(因为脏数据),处理过后恢复正常。

    f1fd838df5d6df54f1ea5aea9bcd47b5.png

    2.3.3 内存快照对比

    为了更有效率的找出内存泄露的对象,一般会获取两个堆转储文件(先 dump 一个,隔段时间再 dump 一个),通过对比后的结果可以很方便定位。

    235eab4b5963224ed4e88777c15aee6b.png
    014a50910d0532bddaac861b59b01ee5.png
    展开全文
  • 前言在这个系列的前四篇文章中,我分别介绍了DVM、ART、内存泄漏和内存检测工具的相关知识点,这一篇我们通过一个小例子,来学习如何使用内存分析工具MAT。1.概述在进行内存分析时,我们可以使用Memory Monitor和...

    前言

    在这个系列的前四篇文章中,我分别介绍了DVM、ART、内存泄漏和内存检测工具的相关知识点,这一篇我们通过一个小例子,来学习如何使用内存分析工具MAT。

    1.概述

    在进行内存分析时,我们可以使用Memory Monitor和Heap Dump来观察内存的使用情况、使用Allocation Tracker来跟踪内存分配的情况,也可以通过这些工具来找到疑似发生内存泄漏的位置。但是如果想要深入的进行分析并确定内存泄漏,就要分析
    疑似发生内存泄漏时所生成堆存储文件。堆存储文件可以使用DDMS或者Memory Monitor来生成,输出的文件格式为hpof,而MAT就是来分析堆存储文件的。
    MAT,全称为Memory Analysis Tool,是对内存进行详细分析的工具,它是Eclipse的插件,如果用Android Studio进行开发则需要单独下载它,下载地址为:http://eclipse.org/mat/,这篇文章MAT的版本为1.6.1。

    2.生成hpof文件

    2.1 准备内存泄漏代码

    我们需要准备一段发生内存泄漏代码,如下所示。

    public class MainActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            LeakThread leakThread = new LeakThread();
            leakThread.start();
        }
        class LeakThread extends Thread {
            @Override
            public void run() {
                try {
                    Thread.sleep(60 * 60 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    上面的代码是很典型的内存泄漏的例子,原因就是非静态内部类LeakThread持有外部类MainActivity的引用,LeakThread中做了耗时操作,导致MainActivity无法被释放,关于内存泄漏可以查看Android内存优化(三)避免可控的内存泄漏这篇文章。

    2.2 DDMS生成hpof文件

    生成hpof文件主要分为以下几个步骤:

    1. 在Android Studio中打开DDMS,运行程序。
    2. 在Devices中选择要分析的应用程序进程,点击Update Heap按钮(装有一半绿色液体的圆柱体)开始进行追踪。
    3. 进行可能发生内存问题的操作(本文的例子就是不断的切换横竖屏)。
    4. 点击Dump HPROP File按钮结束追踪,生成并保存hprof文件,如下图所示。

    DDMS生成的hprof文件并不是标准的,还需要将它转换为标准的hprof文件,这样才会被MAT识别从而进行分析,可以使用SDK自带的hprof-conv进行转换,它的路径在sdk/platform-tools中,进入到该路径执行以下语句即可:

    hprof-conv D:\before.hprof D:\after.hprof
    

    其中 D:\before.hprof 是要转换的hprof文件路径,D:\after.hprof 则是转换后hprof文件的保存路径。

    2.3 Memory Monitor生成hpof文件

    除了用DDMS来生成hpof文件,还可以用AS的Memory Monitor来生成hpof文件。
    生成hpof文件主要分为一下几个步骤:

    1. 在Android Monitor中选择要分析的应用程序进程。
    2. 进行可能发生内存问题的操作(本文的例子就是不断的切换横竖屏)。
    3. 点击Dump Java Heap按钮,生成hprof文件,如下图所示。

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

    3.MAT分析hpof文件

    用MAT打开标准的hpof文件,选择Leak Suspects Report选项。这时MAT就会生成报告,这个报告分为两个标签页,一个是Overview,一个是Leak Suspects(内存泄漏猜想),如下图所示。

    Leak Suspects中会给出了MAT认为可能出现内存泄漏问题的地方,上图共给出了3个内存泄漏猜想,通过点击每个内存泄漏猜想的Details可以看到更深入的分析清理情况。如果内存泄漏不是特别的明显,通过Leak Suspects是很难发现内存泄漏的位置。

    打开Overview标签页,首先看到的是一个饼状图,它主要用来显示内存的消耗,饼状图的彩色区域代表被分配的内存,灰色区域的则是空闲内存,点击每个彩色区域可以看到这块区域的详细信息,如下图所示。

    再往下看,Actions一栏的下面列出了MAT提供的四种Action,其中分析内存泄漏最常用的就是Histogram和Dominator Tree。我们点击Actions中给出的链接或者在MAT工具栏中就可以打开Dorminator Tree和Histogram,如下图所示。

    其中左边第二个选项是Histogram,第三个选项是Dorminator Tree,第四个是OQL,下面分别对它们进行介绍。

    3.1 Dominator Tree

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

    图中可以看出Dorminator Tree有三列数据。

    • Shallow Heap:对象自身占用的内存大小,不包括它引用的对象。如果是数组类型的对象,它的大小是数组元素的类型和数组长度决定。如果是非数组类型的对象,它的大小由其成员变量的数量和类型决定。
    • Retained Heap:一个对象的Retained Set所包含对象所占内存的总大小。换句话说,Retained Heap就是当前对象被GC后,从Heap上总共能释放掉的内存。

    Retained Set指的是这个对象本身和他持有引用的对象以及这些引用对象的Retained Set所占内存大小的总和,官方的图解如下所示。

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

    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的内存才会被释放。

    这里对支配树进行了讲解,我们可以得出一个结论:通过MAT提供的Dominator Tree,可以很清晰的得到一个对象的直接支配对象,如果直接支配对象中出现了不该有的对象,就说明发生了内存泄漏。
    在Dominator Tree的顶部Regex可以输入过滤条件(支持正则表达式),如果是查找Activity内存泄漏,可以在Regex中输入Activity的名称,比如我们这个例子可以输入MainActivity,效果如下图所示。


    Dominator Tree中列出了很多MainActivity实例,MainActivity是不该有这么多实例的,基本可以断定发生了内存泄漏,具体内存泄漏的原因,可以查看GC引用链。在MainActivity一项单击鼠标右键,选择Path To GC Roots,如下图所示。

    Path To GC Roots选项用来表示从对象到GC Roots的路径,根据引用类型会有多种选项,比如with all references就是包含所有的引用,这里我们选择exclude all phantom/weak/soft etc. references,因为这个选项排除了虚引用、弱引用和软引用,这些引用一般是可以被回收的。这时MAT就会给出MainActivity的GC引用链。


    引用MainActivity的是LeakThread,this$0的含义就是内部类自动保留的一个指向所在外部类的引用,而这个外部类就是MainActivity,这将会导致MainActivity无法被GC。

    3.2 Histogram

    Histogram与Dominator Tree不同的是,Dominator Tree是在对象实例的角度上进行分析,注重引用关系分析,而Histogram则在类的角度上进行分析,注重量的分析。
    Histogram中的内容如下图所示。

    可以看到Histogram中共用四列数据,关于Shallow Heap和Shallow Heap的含义我们在3.1节已经知道了,剩余的 Class Name代表类名,Objects代表对象实例的个数。

    在Histogram的顶部Regex同样可以输入过滤条件,这里同样输入MainActivity,效果如下图所示。

    MainActivity和LeakThread实例各为11个,基本上可以断定发生了内存泄漏。具体内存泄漏的原因,同样可以查看GC引用链。在MainActivity一项单击鼠标右键,选择Merge Shortest Paths to GC roots ,并在选项中选择exclude all phantom/weak/soft etc. references如下图所示。

    Histogram是在类的角度进行分析,而Path To GC Roots是用来分析单个对象的,因此在Histogram无法使用Path To GC Roots查询,可以使用Merge Shortest Paths to GC roots查询,它表示从GC roots到一个或一组对象的公共路径。
    得出的结果和3.1节是相同的,引用MainActivity的是LeakThread,这导致了MainActivity无法被GC。

    3.3 OQL

    OQL全称为Object Query Language,类似于SQL语句的查询语言,能够用来查询当前内存中满足指定条件的所有的对象。它的查询语句的基本格式为:

    SELECT * FROM [ INSTANCEOF ]	<class_name> [ WHERE <filter-expression>]
    

    当我们输入select * from instanceof android.app.Activity并按下F5时(或者按下工具栏的红色叹号),会将当前内存中所有Activity都显示出来,如下图所示。


    如果Activty比较多,或者你想查找具体的类,可以直接输入具体类的完整名称:

    select * from com.example.liuwangshu.leak.MainActivity
    

    通过查看GC引用链也可以找到内存泄漏的原因。关于OQL语句有很多用法,具体可以查看官方文档

    3.4 对比hpof文件

    因为我们这个例子很简单,可以通过上面的方法来找到内存泄漏的原因,但是复杂的情况就需要通过对比hpof文件来进行分析了。使用步骤为:

    1. 操作应用,生成第一个hpof文件。
    2. 进行一段时间操作,再生成第二个hpof文件。
    3. 用MAT打开这两个hpof文件。
    4. 将第一个和第二个hpof文件的Dominator Tree或者Histogram添加到Compare Basket中,如下图所示。
    5. 在Compare Basket中点击红色叹号按钮生成Compared Tables,Compared Tables如下图所示。

    在Compared Tables也有顶部Regex,输入MainActivity进行筛选。

    可以看到MainActivity在这一过程中增加了6个,MainActivity的实例不应该增加的,这说明发生了内存泄漏,可以通过查看GC引用链来找到内存泄漏的具体的原因。

    除了上面的对比方法,Histogram还可以通过工具栏的对比按钮来进行对比:

    生成的结果和Compared Tables类似,我们输入MainActivity进行筛选:

    可以看到第二个hpof文件比第一个hpof文件多了6个MainActivity实例。

    MAT还有很多功能,这里也只介绍了常用的功能,其他的功能就需要读者在使用过程中去发现并积累。

    参考资料
    《Android群英传 神兵利器》
    《Android应用性能优化最佳实践》
    《高性能Android应用开发》
    利用MAT进行内存泄露分析
    Android最佳性能实践(二)——分析内存的使用情况
    Memory Analyzer

    分享到 评论 上一篇
    《Android进阶之光》勘误
    下一篇
    React Native组件(四)TextInput组件解析
    展开全文
  • Mac OS java内存分析工具,Eclipse MAT(Memory analyse tool)
  • android内存分析工具MAT的使用 当遇到OutOfMemory问题的时候,怎么样通过MAT来定位和分析问题呢?先看个例子: public class MemoryLeakActivity extends AppCompatActivity{ @Override protected void ...
  • [Android Memory] 内存分析工具 MAT 的使用 转载自: http://blog.csdn.net/aaa2832/article/details/19419679 1 内存泄漏的排查方法   Dalvik Debug Monitor Server (DDMS) 是 ADT插件的一部分...
  • 本系列共计3篇:《JVM 内存分析工具 MAT 的深度讲解与实践——入门篇》介绍 MAT 产品功能、基础概念、与其他工具对比、Quick Start 指南。《JVM 内存分析工具 MAT 的深度讲解与实践——进阶篇》展开并详细介绍 MAT ...
  • 《JVM 内存分析工具 MAT 的深度讲解与实践——入门篇》介绍 MAT 产品功能、基础概念、与其他工具对比、Quick Start 指南。 《JVM 内存分析工具 MAT 的深度讲解与实践——进阶篇》展开并详细介绍 .
  • eclipse内存分析工具MAT使用  MAT(Memory Analyzer Tool) 是基于heap dumps来进行分析的,所以首先必须通过一定的手段得到JAVA堆的DUMP文件。JDK自带的。JConsole 或者 JMAP都是不错的工具。   准备   (·...
  • 在这个系列的前四篇文章中,我分别介绍了DVM、ART、内存泄漏和内存检测工具的相关知识点,这一篇我们通过一个小例子,来学习如何使用内存分析工具MAT。 1.概述 在进行内存分析时,我们可以使用Memory Monitor和...
  • 在spark streaming程序持续运行中,经过一段时间之后,executor频繁发生GC time,导致每个批次处理时间变长,推测程序存在内存泄漏,因此引入MAT内存分析工具 MAT的安装 由于我们已经习惯了IDEA开发,为了MAT下载个...
  • 内存分析工具MAT介绍

    2019-08-12 10:27:24
    MAT(Memory Analyzer Tool)是一个基于Eclipse的内存分析工具,是一个快速、功能丰富的java heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗。 官网地址:https://www.eclipse.org/mat/ 这里我们下载...
  • MAT(Memory Analyzer Tools)是一个快速且功能丰富的 Java 堆分析器,可帮助您查找内存泄漏...1 简介MAT 是一款非常强大的内存分析工具,在 Eclipse 中有相应的插件,同时也有单独的安装包。在进行内存分析时,只要获...
  • 本文详细讲解 MAT 众多内存分析工具功能,这些功能组合使用异常强大,熟练使用几乎可以解决所有的堆内存离线分析的问题。我们将功能划分为4类:内存分布详情、对象间依赖、对象状态详情、按条件检索。每大类有多个...
  • MAT下载安装: 1.在eclipse中安装插件 ...MAT分析的是hprof文件,hprof文件记录了JVM内存溢出时的堆信息,通过分析该文件我们可以分析溢出原因。 JVM参数配置: -Xms10M -Xmx10M -XX:+HeapDumpOnOutOfMemoryEr...
  • eclipse内存分析工具MAT浅谈 博客分类:  开发工具 javaeclipsejvmmemory analyzer  MAT(Memory Analyzer Tool) 是基于heap dumps来进行分析的,所以首先必须通过一定的手段得到JAVA堆的DUMP文件。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,550
精华内容 620
关键字:

内存分析工具mat