热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android内存分析工具(三):MAT

如果需要能更精确定位问题的堆转储,可以在应用代码中调用dumpHprofData()来生成堆转储。主要功能:1.Overview标签页,提供一个概览界面。2.Histogram视
  • 如果需要能更精确定位问题的堆转储,可以在应用代码中调用dumpHprofData()来生成堆转储。

  • 主要功能:
    1.Overview标签页,提供一个概览界面。
    2.Histogram视图,MAT最有用的工具之一,它可以列出任意一个类的实例数。查找内存泄露或者其他内存方面问题是,首先看看最有可能出问题的类,这个类有多少个实例是个比较好的选择。它支持使用正则表达式来查找某个特定的类,还可以计算出该类所有对象的保留堆最小值或者精确值。当选择了某条显示条目后,可以通过右击弹出菜单。在诊断内存相关问题时,这个菜单是个非常重要的工具。如果开发者怀疑这里有个内存泄露,可以通过菜单直接查看该类的对象持有哪些其他对象,当然,MAT支持过滤查询结果(比如说限制被持有对象的类型)。查询结果出来时,列表通过另外一个有用的工具-”Path toGC Roots”-展示给开发人员。它支持多种过滤选项,比如说排除弱引用-这是最常见的一个选项,因为当GC运行时,被弱引用持有的对象会被GC直接回收,所以这种对象是不会造成内存泄露的,一般直接把这种信息排除。如果MAT预定义的查询不能满足用户需求的话,它还支持自己定制查询,定制的自由度非常大,拥有无限的可能。
    3.支配树(Dominator Tree),提供程序中最占内存的对象 (described later in the article)。支配树可以算是MAT中第二有用的工具,它可以将所有对象按照保留堆大小排序显示。用户可以直接在“Overview”选项页中点击“Dominator Tree”进入该工具,也可以在上面提到的菜单中选择“immediate dominators”进入该工具。前者显示dump文件中所有的对象,后者会从类的层面上查找并聚合所有支配关系。该界面每一行的最左边都有一个文件型的图标,这些图标有的左下角带有一个红色的点,有的则没有。带有红点的对象就表示是可以被GC Roots访问到的,而可以被GC Root访问到的对象都是无法被回收的。那么这就说明所有带红色的对象都是泄漏的对象吗?当然不是,因为有些对象系统需要一直使用,本来就不应该被回收。我们可以注意到,上图当中所有带红点的对象最右边都有写一个System Class,说明这是一个由系统管理的对象,并不是由我们自己创建并导致内存泄漏的对象。而不带有红点的对象,也可能被 GC 间接持有。总的来说,Dominator Tree中比较常用的一种分析方式,即搜索大内存对象通向GC Roots的路径,因为内存占用越高的对象越值得怀疑。
    4.对象查询语言(Object Query Language Studio), 用来写MAT查询的工具.
    5.专家系统测试(Expert System Test) –
    6.堆Dump概况(Heap Dump Overview) –提供堆dump文件的详细信息
    7.疑似泄露点(Leak Suspects) – 提供内存泄露疑点占用内存大小,被谁加载的,以及类型等详细信息。
    8.Top Components – 提供占内存最多的对象信息,还包括可能的内存浪费信息.
    9.查询浏览器(Query Browser) – 提供很多很有用的查询,有助于内存分析,本文将会介绍最有用的那些查询。根据地址查找对象 – 可以根据提供的一个地址查找某个特定的对象.
    10.对象列表(List Objects) – 显示应用中所有对象,以及这些对象持有哪些其他对象和被哪些其他对象持有,(MAT会提示查询哪一个对象)。
    11.根据类显示对象(Show Objects by Class) – 列出每个类有多少对象.
    12.到GC根节点的路径(Path to GC Roots) – 显示到根节点的引用路径 (有好多过滤选项).
    13.合并到GC根节点的最短路径(Merge Shortest Paths to GC Roots) –找到从GC根节点到一个对象或一组对象的共同路径。
    14.即时支配(Immediate Dominators) – Finds and aggregates on a class level all objects dominating a given set of objects. 在给定的一组对象中,从类的层面上查找并聚合所有支配关系。(【译者注】好吧,我觉得实在有必要说一下支配的意思,支配在计算机的控制流理论中意思是假如说从起始节点到节点B的所有路径都经过节点A,则节点A支配节点B。在垃圾回收理论中应该是指从某个对象在另外一个对象的保留堆中)
    15.显示保留集合(Show Retained Set) – 计算一个对象的保留堆大小.
    16.饼图 – 显示持有内存最大的对象
    17.直方图 – 显示每个类的对象数量
    18.支配树 – 列出所有对象,并按照对象持有的保留堆大小排序
    19.检查器 – 选择一个对象,并显示其详细信息
    20.即时支配(Immediate Dominators) – Finds and aggregates on a class level all objects dominating a given set of objects. 在给定的一组对象中,从类的层面上查找并聚合所有支配关系。(【译者注】好吧,我觉得实在有必要说一下支配的意思,支配在计算机的控制流理论中意思是假如说从起始节点到节点B的所有路径都经过节点A,则节点A支配节点B。在垃圾回收理论中应该是指从某个对象在另外一个对象的保留堆中)
    21.定制查询:如果MAT预定义的查询不能满足用户需求的话,它还支持自己定制查询,定制的自由度非常大,拥有无限的可能。
    22.“Dominator Tree”与“immediate dominators”,前者显示dump文件中所有的对象,后者会从类的层面上查找并聚合所有支配关系。
    23.查询:是用来检查对象树的基本工具,内存分析就是在许多对象中查找不希望看到的引用关系的过程-这件事听上去容易做起来难。如果可以过滤这些对象和应用关系的话可以使这项复杂的运动简单不少。一个开发人员想要成功的调试内存问题,必须掌握两个关键点。第一个是对自己的应用充分了解,如果对自己应用程序中的对象之间的关系不够了解的话,是不能找到内存问题的。第二个是掌握过滤和查找的技巧。如果开发者知道对象结构,而且也可以快速的找到想要的东西,那么找到那些异常状况将会变得容易一些。MAT 有内建查询,我们也可自定义查询。

  • 这里要说一下的是,Retained Heap并不总是那么有效。例如我在A里new了一块内存,赋值给A的一个成员变量。此时我让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来选择这个组。(又开始Histogram中不显示Retained heap,需要点击那个计算器的按钮才会计算出来)。这里最小的粒度是类级别的。

  • 为了计算Retained Memory,MAT引入了Dominator Tree。加入对象A引用B和C,B和C又都引用到D(一个菱形)。此时要计算Retained Memory,A的包括A本身和B,C,D。B和C因为共同引用D,所以他俩的Retained Memory都只是他们本身。D当然也只是自己。我觉得是为了加快计算的速度,MAT改变了对象引用图,而转换成一个对象引用树。在这里例子中,树根是A,而B,C,D是他的三个儿子。B,C,D不再有相互关系。把引用图变成引用树,计算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。这是个真正的引用图的概念,表示该对象的出节点(被该对象引用的对象)和入节点(引用到该对象的对象)。

  • Java内存泄露归根结底都是一个原因导致的,应该被释放的对象被生命期更长的对象引用,所以没法被GC。这个生命期更长的对象很常见的是static对象,会持续整个进程。在个人实际工作中,我会先用adb shell dumpsys meminfo查看dalvik heap会不会持续增长。如果是,我会在在dominator Tree中按照Retained Memory排序,找出比较大的(经常是Bitmap),然后用Path to GC Roots看看其引用情况。在这个Path中,一般会发现我们app自己包的类,可以分析这个类是不是还是需要的。如果不需要,那说明可能存在内存泄露。此时,在对这个自己包的类查看incoming references。看看到底是哪些引用导致它没有释放。用这种方法,会比较快的发现问题。另外一个可以减少内存的方法是删除临时不用的内存,也可以使用mat像监测内存泄露一样,看看哪些比较大的内存临时不用却仍然被引用,然后删除对其引用。

  • Histogram:以下图示为分析内存泄露过程

将所有的类列出来

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

  • Histogram对比:

这里写图片描述
如上图,为查找内存泄漏,通常需要两个 Dump结果作对比,打开 Navigator History面板,将两个表的 Histogram结果都添加到 Compare Basket中去

这里写图片描述
如上图,添加好后,打开 Compare Basket面板,得到结果

这里写图片描述
如上图,点击右上角的 ! 按钮,将得到比对结果

这里写图片描述
如上图,为更方便查找差异,可以调整对比选项

这里写图片描述
如上图,再把对比的结果排序,就可得到直观的对比结果

  • MAT中常用查询:

List objects:
With Outgoing References 显示选中对象持有哪些对象.
With Incoming References 显示选中对象被哪些对象持有。[如果一个类有很多不需要的实例,那么可以找到哪些对象持有该对象,让这个对象没法被回收]

Show object by class:
With Outgoing References 显示选中对象持有哪些对象, 这些对象按类合并在一起排序
With Incoming References 显示选中对象被哪些对象持有.这些对象按类合并在一起排序

Path to GC Roots:
With all references 显示选中对象到GC根节点的引用路径,包括所有类型引用.
Exclude weak references 显示选中对象到GC根节点的引用路径,排除了弱引用. [弱引用不会影响GC回收对象]
Exclude soft references 显示选中对象到GC根节点的引用路径,排除软引用(【译者注】软引用持有的对象在内存空间足够时,GC不回收,内存空间足够时,GC回收)
Exclude phantom references 显示选中对象到GC根节点的引用路径,排除虚引用(【译者注】虚引用是最弱的引用,get()总是返回null,当它的对象被GC回收时,GC将reference放在ReferenceQueue中,用户代码当发现这个reference在在ReferenceQueue时就知道它持有的对象已经被回收了,这时可以做一些清理工作。《Java编程思想》第四版,中文版,第87页写到Java的finilize方法是为了对象被回收前做清理工作,但是事实上会有隐患,虚引用正是弥补)

Java Basics:
classloader 该对象对应的classloader信息
find String 在这个对象中查询需要的字符串(还不确定,需要再搞下)
group by 根据某个字段统计出现的个数
References Statistics Class Loader Explorer 显示引用和对象的统计信息,列出类加载器,包括定义的类
Customized Retained Set 计算选中对象的保留堆,排除指定的引用
Open in Dominator Tree 对选中对象生成支配树
Show as Histogram 展示任意对象的直方图
Thread Details 显示线程的详细信息和属性
Thread Overview and Stacks 线程堆栈

Java Collections:
Array Fill Ratio 输出数组中,非基本类型、非null对象个数占数组总长度的比例。
Arrays Grouped by Size 显示数组的直方图,按大小分组
Collection Fill Ratio 输出给定集合中,非基本类型、非null对象个数占集合容量的比例。
Collections Grouped by Size 显示集合的直方图,按大小分组
Extract Hash Set Values 列出指定hash集合中的元素
Extract List Values 列出指定LinkedList,ArrayList或Vector中的元素
Hash Entries 展开显示指定HashMap或Hashtable中的键值对
Map Collision Ratio 输出指定的映射集合的碰撞率
Primitive Arrays With a Constant Value 列出基本数据类型的数组,这些数组是由一个常数填充的。

Leak Identification:
Component Report
Top Consumers 分析可能的内存浪费或者低效使用的组件,并输出最大的那个

  • 使用MAT比较heap dumps
    调试内存泄露时,有时候适时比较2个地方的heap状态是很有用的。这时你就需要生成2个单独的HPROF文件(不要忘了转换格式)。下面是一些关于如何在MAT里比较2个heapdumps的内容(有一点复杂):
    a) 第一个HPROF文件(usingFile>OpenHeapDump).
    b) 打开Histogram view.
    c) 在Navigation Historyview里(如果看不到就从Window>NavigationHistory找).右击histogram然后选择AddtoCompareBasket.
    d) 打开第二个HPROF文件然后重做步骤2和3.
    e) 切换到CompareBasketview,然后点击ComparetheResults(视图右上角的红色”!”图标)。

推荐阅读
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • LeetCode笔记:剑指Offer 41. 数据流中的中位数(Java、堆、优先队列、知识点)
    本文介绍了LeetCode剑指Offer 41题的解题思路和代码实现,主要涉及了Java中的优先队列和堆排序的知识点。优先队列是Queue接口的实现,可以对其中的元素进行排序,采用小顶堆的方式进行排序。本文还介绍了Java中queue的offer、poll、add、remove、element、peek等方法的区别和用法。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 数组的排序:数组本身有Arrays类中的sort()方法,这里写几种常见的排序方法。(1)冒泡排序法publicstaticvoidmain(String[]args ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • 本文介绍了使用哈夫曼树实现文件压缩和解压的方法。首先对数据结构课程设计中的代码进行了分析,包括使用时间调用、常量定义和统计文件中各个字符时相关的结构体。然后讨论了哈夫曼树的实现原理和算法。最后介绍了文件压缩和解压的具体步骤,包括字符统计、构建哈夫曼树、生成编码表、编码和解码过程。通过实例演示了文件压缩和解压的效果。本文的内容对于理解哈夫曼树的实现原理和应用具有一定的参考价值。 ... [详细]
  • JavaScript和HTML之间的交互是经由过程事宜完成的。事宜:文档或浏览器窗口中发作的一些特定的交互霎时。能够运用侦听器(或处置惩罚递次来预订事宜),以便事宜发作时实行相应的 ... [详细]
  • 使用eclipse创建一个Java项目的步骤
    本文介绍了使用eclipse创建一个Java项目的步骤,包括启动eclipse、选择New Project命令、在对话框中输入项目名称等。同时还介绍了Java Settings对话框中的一些选项,以及如何修改Java程序的输出目录。 ... [详细]
  • HashMap的相关问题及其底层数据结构和操作流程
    本文介绍了关于HashMap的相关问题,包括其底层数据结构、JDK1.7和JDK1.8的差异、红黑树的使用、扩容和树化的条件、退化为链表的情况、索引的计算方法、hashcode和hash()方法的作用、数组容量的选择、Put方法的流程以及并发问题下的操作。文章还提到了扩容死链和数据错乱的问题,并探讨了key的设计要求。对于对Java面试中的HashMap问题感兴趣的读者,本文将为您提供一些有用的技术和经验。 ... [详细]
  • 本文介绍了在Android开发中使用软引用和弱引用的应用。如果一个对象只具有软引用,那么只有在内存不够的情况下才会被回收,可以用来实现内存敏感的高速缓存;而如果一个对象只具有弱引用,不管内存是否足够,都会被垃圾回收器回收。软引用和弱引用还可以与引用队列联合使用,当被引用的对象被回收时,会将引用加入到关联的引用队列中。软引用和弱引用的根本区别在于生命周期的长短,弱引用的对象可能随时被回收,而软引用的对象只有在内存不够时才会被回收。 ... [详细]
  • Hibernate延迟加载深入分析-集合属性的延迟加载策略
    本文深入分析了Hibernate延迟加载的机制,特别是集合属性的延迟加载策略。通过延迟加载,可以降低系统的内存开销,提高Hibernate的运行性能。对于集合属性,推荐使用延迟加载策略,即在系统需要使用集合属性时才从数据库装载关联的数据,避免一次加载所有集合属性导致性能下降。 ... [详细]
  • 本文总结了在编写JS代码时,不同浏览器间的兼容性差异,并提供了相应的解决方法。其中包括阻止默认事件的代码示例和猎取兄弟节点的函数。这些方法可以帮助开发者在不同浏览器上实现一致的功能。 ... [详细]
author-avatar
岁月如风晓
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有