MAT基础知识

MAT简介

MAT(Memory Analyzer Tool),一个基于Eclipse的内存分析工具,是一个快速、功能丰富的Java heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗。使用内存分析工具从众多的对象中进行分析,快速的计算出在内存中对象的占用大小,看看是谁阻止了垃圾收集器的回收工作,并可以通过报表直观的查看到可能造成这种结果的对象。 
这里写图片描述

当然MAT也有独立的不依赖Eclipse的版本,只不过这个版本在调试Android内存的时候,需要将DDMS生成的文件进行转换,才可以在独立版本的MAT上打开。不过Android SDK中已经提供了这个Tools,所以使用起来也是很方便的。

MAT工具的下载安装

这里是MAT的下载地址:https://eclipse.org/mat/downloads.php,下载时会提供三种选择的方式: 
这里写图片描述

  • Update Site 这种方式后面会有一个网址:比如http://download.eclipse.org/mat/1.4/update-site/ ,安装过Eclipse插件的同学应该知道,只要把这段网址复制到对应的Eclipse的Install New Software那里,就可以进行在线下载了。 
    这里写图片描述

  • Archived Update Site 这种方式安装的位置和上一种差不多,只不过第一种是在线下载,这一种是使用离线包进行更新,这种方式劣势是当这个插件更新后,需要重新下载离线包,而第一种方式则可以在线下载更新。

  • Stand-alone Eclipse RCP Applications 这种方式就是把MAT当成一个独立的工具使用,不再依附于Eclipse,适合不使用Eclipse而使用Android Studio的同学。这种方式有个麻烦的地方就是DDMS导出的文件,需要进行转换才可以在MAT中打开。

下载安装好之后,就可以使用MAT进行实际的操作了。

MAT中一些概念介绍

要看懂MAT的列表信息,Shallow heap、Retained Heap、GC Root 这几个概念一定要弄懂。

1、Shallow heap

Shallow size就是对象本身占用内存的大小,不包含其引用的对象。

  1. 常规对象(非数组)的Shallow size有其成员变量的数量和类型决定。
  2. 数组的shallow size有数组元素的类型(对象类型、基本类型)和数组长度决定 
    因为不像c++的对象本身可以存放大量内存,java的对象成员都是些引用。真正的内存都在堆上,看起来是一堆原生的byte[],char[], int[],所以我们如果只看对象本身的内存,那么数量都很小。所以我们看到Histogram图是以Shallow size进行排序的,排在第一位第二位的是byte,char 。

2、Retained Heap

Retained Heap的概念,它表示如果一个对象被释放掉,那么该对象引用的所有对象(包括被递归释放的)占用的heap也会被释放。 
举例: 
如果一个对象的某个成员new了一大块int数组,那这个int数组也可以计算到这个对象中。与shallow heap比较,Retained heap可以更精确的反映一个对象实际占用的大小(因为如果该对象释放,retained heap都可以被释放)。

Retained Heap并不总是那么有效。 
举例:

  1. 我在A里new了一块内存,赋值给A的一个成员变量。

  2. 我让B也指向这块内存。

    此时,因为A和B都引用到这块内存,所以A释放时,该内存不会被释放。所以这块内存不会被计算到A或者B的Retained Heap中。

为了纠正这点,MAT中的Leading Object(例如A或者B)不一定只是一个对象,也可以是多个对象。此时,(A,B)这个组合的Retained Set就包含那块大内存了。对应到MAT的UI中,在Histogram中,可以选择Group By class, superclass or package来选择这个组。

为了计算Retained Memory,MAT引入了Dominator Tree

举例:

  • 对象A引用B和C,B和C又都引用到D(一个菱形)。

  • 计算Retained Memory

    • A的包括A本身和B,C,D。
    • B和C因为共同引用D,所以B,C 的Retained Memory都只是他们本身。
    • D当然也只是自己。

在这里例子中,树根是A,而B,C,D是他的三个儿子。B,C,D不再有相互关系。

我觉得是为了加快计算的速度,MAT将对象引用图转换成对象引用树。把引用图变成引用树,计算Retained Heap就会非常方便,显示也非常方便。对应到MAT UI上,在dominator tree这个view中,显示了每个对象的shallow heap和retained heap。然后可以以该节点为树根,一步步的细化看看retained heap到底是用在什么地方了。

这种从图到树的转换确实方便了内存分析,但有时候会让人有些疑惑。本来对象B是对象A的一个成员,但因为B还被C引用,所以B在树中并不在A下面,而很可能是平级。

为了纠正这点,MAT中点击右键,可以List objects中选择with outgoing references和with incoming references。这是个真正的引用图的概念,

  • outgoing references :表示该对象的出节点(被该对象引用的对象)。
  • incoming references :表示该对象的入节点(引用到该对象的对象)。

为了更好地理解Retained Heap,下面引用一个例子来说明:

把内存中的对象看成下图中的节点,并且对象和对象之间互相引用。这里有一个特殊的节点GC Roots,这就是reference chain(引用链)的起点: 
这里写图片描述 
这里写图片描述

从obj1入手,上图中蓝色节点代表仅仅只有通过obj1才能直接或间接访问的对象。因为可以通过GC Roots访问,所以上图的obj3不是蓝色节点;而在下图却是蓝色,因为它已经被包含在retained集合内。

  • 上图中obj1的retained size是obj1、obj2、obj4的shallow size总和;
  • 下图的retained size是obj1、obj2、obj3、obj4的shallow size总和。 
    obj2的retained size是obj3、obj4的shallow size总和。

3、GC Root

  • GC发现通过任何reference chain(引用链)无法访问某个对象的时候,该对象即被回收。

名词GC Roots正是分析这一过程的起点,例如JVM自己确保了对象的可到达性(那么JVM就是GC Roots),所以GC Roots就是这样在内存中保持对象可到达性的,一旦不可到达,即被回收。

通常GC Roots是一个在current thread(当前线程)的call stack(调用栈)上的对象(例如方法参数和局部变量),或者是线程自身或者是system class loader(系统类加载器)加载的类以及native code(本地代码)保留的活动对象。所以GC Roots是分析对象为何还存活于内存中的利器。

MAT主界面介绍

这里介绍的不是MAT这个工具的主界面,而是导入一个文件之后,显示OverView的界面。

一、打开经过转换的hprof文件:

这里写图片描述

如果选择了第一个,则会生成一个报告。这个无大碍。

这里写图片描述

二、 选择OverView界面:

这里写图片描述

我们需要关注的是下面Actions区域,介绍4种分析方法:

  1. Histogram:列出内存中的对象,对象的个数以及大小

    这里写图片描述

  2. Dominator Tree:列出最大的对象以及其依赖存活的Object (大小是以Retained Heap为标准排序的) 
    这里写图片描述

  3. Top Consumers : 通过图形列出最大的object 
    这里写图片描述

  4. Duplicate Class:通过MAT自动分析泄漏的原因

一般Histogram和 Dominator Tree是最常用的。

三、打开default_report窗口。

该窗口列出了,可能有问题的代码片段。点击Details可以查看相关的详情。 
这里写图片描述

  1. 在Shortest Paths To the Accumulation Point的列表中,我们可以追溯到问题代码的类树的结构,并找到自己代码中的类。 
    在列表中,有两列Shallow Heap和Retained Heap。Shallow Heap指的是所有的实例的内存总和。Retained Heap指的是所有类实例被分配的内存总和,里面包括它们所有引用的对象。 
    这里写图片描述

  2. 在Accumulated Objects列表中,我们可以看见创建的大量的对象。 
    这里写图片描述

  3. 在Accumulated Objects by Class列表中,我们能看见创建大量对象相关的类。 
    这里写图片描述

MAT中的一些有用的视图

1、Thread OvewView

Thread OvewView可以查看这个应用的Thread信息: 
这里写图片描述

2、Group

在Histogram和Domiantor Tree界面,可以选择将结果用另一种Group的方式显示(默认是Group by Object),切换到Group by package,可以更好地查看具体是哪个包里的类占用内存大,也很容易定位到自己的应用程序。 
这里写图片描述

3、Path to GC Root

在Histogram或者Domiantor Tree的某一个条目上,右键可以查看其GC Root Path:

这里写图片描述

点击Path To GC Roots –> with all references 
这里写图片描述

这里也要说明一下Java的引用规则: 
从最强到最弱,不同的引用(可到达性)级别反映了对象的生命周期。

  • Strong Ref(强引用):通常我们编写的代码都是Strong Ref,于此对应的是强可达性,只有去掉强可达,对象才被回收。

  • Soft Ref(软引用):对应软可达性,只要有足够的内存,就一直保持对象,直到发现内存吃紧且没有Strong Ref时才回收对象。一般可用来实现缓存,通过java.lang.ref.SoftReference类实现。

  • Weak Ref(弱引用):比Soft Ref更弱,当发现不存在Strong Ref时,立刻回收对象而不必等到内存吃紧的时候。通过java.lang.ref.WeakReference和java.util.WeakHashMap类实现。

  • Phantom Ref(虚引用):根本不会在内存中保持任何对象,你只能使用Phantom Ref本身。一般用于在进入finalize()方法后进行特殊的清理过程,通过 java.lang.ref.PhantomReference实现。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值