MAT内存分析工具

MAT是MemoryAnalyzerTool的简称,它是一款功能强大的Java堆内存分析器,可以用于查找内存泄漏以及查看内存消耗情况。MAT是

基于Eclipse开发的一款免费的性能分析工具,读者可以在
http://www.eclipse.org/mat/上下载并使用MAT。

一,初识MAT

在分析堆快照前,首先需要导出应用程序的堆快照。在本书前文中提到的jmap、JConsole和VisualVM等工具都可以用于获得Java应用程序的堆快照文件。此外,MAT本身也具有这个功能。

如图6.66所示,在File菜单中选择AcquireHeapDump命令,在弹出对话框的当前Java应用程序列表中选择要分析的应用程序即可,如图6.67所示。

除了直接在MAT中导出正在运行的应用程序堆快照外,也可以通过File菜单中的OpenHeapDump命令打开一个既存的堆快照文件。

注意:使用MAT既可以打开一个已有的堆快照,也可以直接从活动Java程序中导出堆快照。

图6.68所示为正常打开堆快照文件后的MAT界面。

在图6.68的右侧界面中显示了堆快照文件的大小、类、实例和ClassLoader的总数;饼图中显示了当前堆快照中最大的对象。将光标悬停在饼图中,可以在左侧的Inspector界面中查看该对象的详细信息。在饼图中单击,可以对选中的对象进行更多的操作。

单击工具栏上的柱状图按钮(如图6.69所示),可以查看当前堆的类信息,包括类的对象数量、浅堆(Shallow)大小和深堆(Retained)大小,如图6.70所示。

通过柱状图界面,可以查找引用选中对象的对象集合以及选中对象所引用的对象集合。如图6.71所示,选中java.util.Vector对象并右击,在弹出的右键菜单中选择Listobjects命令,弹出的withoutgoingreferences和withincomingreferences子命令分别表示查找java.util.Vector实例的引用对象,以及引用java.util.Vector实例的对象。

注意:通过MAT,可以根据对象间的引用关系对内存中的对象进行分析。

图6.72显示了选择withincomingreferences命令后的输出结果,展示了两个被主线程引用的java.util.Vector局部变量实例。

为了方便查看,柱状图还可以根据ClassLoader和包对类进行排序。图6.73显示了MAT的柱状图排序功能,以及一个按照包进行排序的柱状图输出命令。

图6.72 引用关系查询结果

二,浅堆和深堆

浅堆(ShallowHeap)和深堆(RetainedHeap)是两个非常重要的概念,它们分别表示一个对象结构所占用的内存大小和一个对象被执行GC操作后,可以真实释放的内存大小。

浅堆是指一个对象所消耗的内存。在32位系统中,一个对象引用会占据4个字节,一个int类型会占据4个字节,long型变量会占据8个字节,每个对象头需要占用8个字节。

根据堆快照格式不同,对象的大小可能会向8字节进行对齐。以String对象为例,图6.74显示了String对象的几个属性。

3个int类型以及一个引用类型合计占用的内存为3×4+4=16字节,再加上对象头的8个字节,因此String对象占用的空间,即浅堆的大小是16+8=24字节。浅堆的大小只与对象的结构有关,与对象的实际内容无关。也就是说,无论字符串的长度是多少,内容是什么,浅堆的大小始终是24字节。

深堆的概念略微复杂。要理解深堆,首先需要了解保留集(RetainedSet)。对象A的保留集指当对象A被垃圾回收后,可以被释放的所有的对象集合(包括对象A本身),即对象A的保留集可以被认为是只能通过对象A被直接或者间接访问到的所有对象的集合。通俗地说,就是指仅被对象A所持有的对象的集合。深堆是指对象的保留集中所有对象的浅堆之和。

注意:浅堆指对象本身占用的内存,不包括其内部引用对象的大小。一个对象的深堆指只能通过该对象访问到的(直接或间接)所有对象的浅堆之和,即对象被回收后,可以释放的真实空间。

下面这个例子很好地诠释了深堆的概念。首先是表示点的类定义:

接着是表示线的类定义:

主函数构造了a、b、c、d、e、f、g这7个点,以及aLine、bLine、cLine和dLine这4条线,并在程序最后将a、b、c、d、e这5个点设置为null。具体代码如下:

这段代码的对象引用关系如图6.75所示,其中a、b、c、d、e对象在使用完成后被设置为null。

根据Point类的结构,一个Point实例的浅堆大小为4×2+8=16字节,一个Line实例的浅堆大小为4×2+8=16字节。使用MAT得到该示例的内存快照文件,如图6.76所示。为了阅读方便,笔者将代码中的变量名标识到了内存快照中的对象上。

可以看到,所有的Point实例浅堆和深堆的大小都是16字节。而dLine对象,浅堆为16字节,深堆也是16字节,这是因为dLine对象内的两个点f和g没有被设置为null,因此即使dLine被回收,f和g也不会被释放。对象cLine内的引用对象d和e由于仅在cLine内还存在引用,因此只要cLine被释放,d和e必然也作为垃圾被回收,即d和e在cLine的保留集内,因此cLine的深堆为16×2+16=48字节。

对于aLine和bLine对象,由于两者均持有对方的一个点,因此当aLine被回收时,公共点a在bLine中依然有引用存在,故不会被回收,点a不在aLine对象的保留集中,因此aLine的深堆大小为16+16=32字节。对象bLine与aLine完全一致。

在MAT中,无论是在柱状图还是对象列表中,选中对象并右击,在弹出的快捷菜单中都有ShowRetainedSet命令,它可用于显示指定类或者对象的保留集。图6.77和图6.78分别为在bLine对象上进行该操作,以及bLine对象的保留集。

三,支配树

MAT提供了一个称为支配树(DominatorTree)的对象图。支配树体现了对象实例间的支配关系。在对象引用图中,所有指向对象B的路径都经过对象A,则认为对象A支配对象B。如果对象A是离对象B最近的一个支配对象,则认为对象A为对象B的直接支配者。支配树是基于对象间的引用图所建立的,它具有以下基本性质:

·对象A的子树(所有被对象A支配的对象集合)表示对象A的保留集(retainedset)。

·如果对象A支配对象B,那么对象A的直接支配者也支配对象B。

·支配树的边与对象引用图的边不直接对应。

如图6.79所示,左图表示对象引用图,右图表示左图所对应的支配树。对象A和B由根对象直接支配,由于在到对象C的路径中可以经过A,也可以经过B,因此对象C的直接支配者也是根对象。对象F与对象D相互引用,因为到对象F的所有路径必然经过对象D,因此对象D是对象F的直接支配者。而到对象D的所有路径中,必然经过对象C,即使是从对象F到对象D的引用,从根节点出发,也是经过对象C的,所以对象D的直接支配者为对象C。

同理,对象E支配对象G。到达对象H的路径可以通过对象D,也可以通过对象E,因此对象D和E都不能支配对象H,而经过对象C既可以到达D也可以达到E,因此对象C为对象H的直接支配者。

在MAT中,单击工具栏上的对象支配树按钮,如图6.80所示,可以打开对象支配树视图。

图6.81显示了对象支配树视图的一部分。该截图显示部分main线程对象的直接支配对象,即main线程对象被回收后将被释放的所有对象的集合。

注意:在对象支配树中,某一个对象的子树表示在该对象被回收后也将被回收的对象的集合。

四,垃圾回收根

在Java系统中,作为垃圾回收的根节点可能是以下对象之一。

·系统类:被
bootstrap/systemClassLoader加载的类,例如在rt.jar包中的所有类。

·JNI局部变量:本地代码中的局部变量,例如用户自定义的JNI代码或者JVM内部代码。

·JNI全局变量:本地代码中的全局变量。

·线程:开始,并且没有停止的线程。

·在用同步锁:作为锁的对象。例如调用了wait()或者notify()方法的对象,或者调用了synchronized(Object)操作的对象。

·Java局部变量:如函数的输入参数及方法中的局部变量。

·本地栈:本地代码中的输入、输出参数,例如用户自定义的JNI代码或者JVM内部代码。

·Finalizer:在等待队列中将要被执行析构函数的对象。

·Unfinalized:拥有析构函数,但是没有被析构且不在析构队列中的对象。

·不可达对象:从任何一个根对象都无法到达的对象。但为了能够在MAT中分析,被MAT标志为根。

·未知对象:未知的根类型,用于处理一些特殊的堆格式。

通过MAT,可以列出所有的根对象,如图6.82所示。

五,内存泄漏检测

MAT提供了自动检测内存泄漏,以及统计堆快照内对象分布情况的工具。图6.83展示了内存泄漏检测工具的使用方法。选择菜单中的LeakSuspects命令,MAT会自动生成一份报告。这份报告罗列了系统内可能存在内存泄漏的问题点。图6.84展示了报告中给出的一个问题点样例。

注意:仔细阅读MAT给出的内存泄漏报告,可以帮助开发人员更快地找到系统的潜在问题。

六,最大对象报告

系统中占用内存最大的几个对象,往往是解决系统性能问题的关键所在。如果应用程序发生内存泄漏,那么泄漏的对象通常会在堆快照中占据很大的比重。因此,查看和分析堆快照中最大的对象具有较高的价值。

在MAT中,可以自动查找并显示消耗内存最多的几个对象。如图6.85所示,通过选择TopConsumers命令,可以打开消耗内存最多的对象的报告,其中主要以饼图和表格的形式来展示。

七,查找支配者

通过MAT,开发人员还可以很方便地查找某一个对象或者类的支配者(有关支配者的概念,可以参考6.7.3节“支配树”)。虽然在支配树页面中拥有完整的信息,但是通过MAT提供的支配者查找功能可以更方便地进行查找。图6.86显示了如何查找对象的支配者。

在选择ImmediateDominators命令后,会弹出一个参数对话框,用于设置查找参数,如图6.87所示。在参数对话框中,注意务必正确输入-skip参数,否则查询结果会忽略所有定义在-skip参数中的类和实例。

ImmediateDominators会输出选中对象的直接支配者(将-skip指定的对象排除在外)。

八,线程分析

在堆快照中,还包括当前的线程信息,通过MAT可以查看这些信息。如图6.88所示,通过ThreadDetails、ThreadOverview和ThreadStacks这3个命令,可以查看线程详情。

图6.89所示为选择ThreadStacks命令后的输出结果,其中显示了当前堆快照中的所有线程及线程引用的对象。

九,集合使用情况分析

MAT提供了一套对集合使用状态进行分析的工具,如图6.90所示。

使用这些工具,可以查看数组、集合的填充率;可以观察集合内的数据;也可以分析哈希表的冲突率。

注意:通过对集合使用情况进行分析,可以更好地了解系统的内存使用情况,查找浪费的内存空间。

选择CollectionFillRatio命令,可以展示给定集合的填充率。图6.91所示为该功能的输出结果,其中显示了填充率为0、20%以下、80%以下和100%以下的集合个数。

通过选择HashEntries命令,可以查看Hash表的内容。图6.92所示为该功能的一个输出示例,其中显示了选中的Hash表的内容。对于表中的Key和Value对象,通过右键快捷菜单,还可以进一步分析它们的引用情况和其他具体信息。

十,扩展MAT

MAT是基于Eclipse开发平台的产品,因此它也具有很好的扩展性。开发者可以使用Eclipse对MAT进行扩展,从而实现符合开发人员需要的功能更加强劲的内存分析工具。通过扩展MAT,读者可以实现诸如自动对象查询、优化界面显示、报表增强等功能。本节将通过一个简单的MAT插件,介绍扩展MAT的基本步骤和方法。

注意:MAT是基于Eclipse的,因此对MAT进行二次开发与开发Eclipse插件非常类似。

在Java中,java.lang.String对象实现是基于内部的value字符数组、偏移量offset和字符串长度count来定义字符串String的真实取值的。如果内部数组value的实际长度很长,而字符串真实长度count的数值很小,则说明这个String的内存使用率不高,存在较为严重的内存浪费。

使用公式count/value.length可以计算当前String对象的内存使用率。在最优情况下,String对象的内存使用率是100%,即表示value数组中的所有字符都是当前字符串的内容。当使用类似String.subString()的函数生成新的字符串时,String对象通过调整offset和count,而非创建新的value数组来生成新的字符串,此时String对象的内存利用率就会下降。

本节中展示的插件将在显示String对象时,展示String对象的内存利用率,帮助开发者快速定位可以优化的字符串对象。

为扩展MAT,首先需要安装MAT程序及Eclipse开发工具。

(1)在Eclipse平台中添加MAT目标平台。在Eclipse中打开对话框:Windows|Preferences|Plug-inDevelopment|TargetPlatform。添加MAT平台,选择Add|Nothing|Next。在目标平台的Locations页面中,添加Installation,并指定MAT的安装路径,如图6.93所示。单击Finish按钮,并选择刚刚添加的MAT平台作为目标平台。图6.94所示为配置完成后的目标平台。

(2)创建一个插件工程。选择File|New|Other|Plug-inproject命令,假设工程名称是MATExtension,其他参数可以使用默认设置。创建完成后,在工程的Dependencies页面中添加org.eclipse.mat.api依赖,如图6.95所示。

(3)添加插件的扩展点。在本例中添加
org.eclipse.mat.api.nameResolver,如图6.96所示。在实际开发中,读者可以根据自己的需要,选择合适的扩展点增强MAT的功能。接着填写扩展点的具体信息,如实现扩展点接口的类名和包名,Eclipse会自动生成指定的类,如图6.97所示。

编辑生成的StringUsageDisplayer类,具体代码如下:

StringUsageDisplayer的功能是当MAT中显示String对象时,计算String对象的count值与value数组的长度比值。注释@Subject指定当前
IClassSpecificNameResolver只对java.lang.String对象有效。

(4)当完成开发后,还需要对插件进行打包。选择File|Export|Plug-inDevelopment|
Deployableplug-insandfragments命令,在打开的对话框中选中要打包的插件,并设置MAT的安装路径进行插件安装,如图6.98所示。

安装完成后,在MAT的plugins目录下就有了MATExtension插件的JAR包。

安装插件后的MAT,可以使用以下OQL查询取得所有内存利用率不是100%的String。

查询结果如图6.99所示,其中不仅显示了字符串的真实取值,也显示了当前字符串的内存使用率,可以帮助开发人员快速定位能够优化的字符串。

注意:通过对MAT的扩展,可以让MAT更贴近实际生产环境,使之更易于使用,提高了堆内存分析的效率。

本文给大家讲解的内容是Java性能调优六大工具:MAT内存分析工具

  1. 下篇文章给大家讲解的内容是Visual VM对OQL的支持
  2. 觉得文章不错的朋友可以转发此文关注小编;
  3. 需要详细的资料可以滴我扣666哟
  4. 感谢大家的支持!

发布即巅峰:Java性能调优六大工具:MAT内存分析工具相关推荐

  1. 千字详解:“Java性能调优六大工具”之JConsole工具

    JConsole工具是JDK自带的图形化性能监控工具. 通过JConsole工具, 可以查看Java应用程序的运行概况, 并监控堆信息. 永久区使用情况及类的加载情况等. 本文主要介绍JConsole ...

  2. 【JVM与性能调优】与Java性能调优相关的JDK命令行工具大盘点

    一. jps 命令 1.1 jps介绍 jps(JVM Process Status Tool) 列出Java进程,显示指定系统内所有的HotSpot虚拟机进程. 查询Linux系统当前所有java进 ...

  3. Java性能调优工具:MAT内存分析工具,上万字带你彻底了解

    MAT内存分析工具 MAT是MemoryAnalyzerTool的简称,它是一款功能强大的Java堆内存分析器,可以用于查找内存泄漏以及查看内存消耗情况.MAT是 基于Eclipse开发的一款免费的性 ...

  4. 为什么对 Java 性能调优最后都像在调 you?

    不知道你有没有发现,优化Java,或者任何其他语言的代码性能经常被当做是一种暗黑艺术. 性能分析有种神秘感.画面类似是这样的:一个「黑客」经过多年练就的手艺,能够快速深入了解某个系统,并提出神奇的解决 ...

  5. java 性能调优_Java性能调优调查结果(第三部分)

    java 性能调优 这是本系列文章的第三篇,我们将分析2014年10月进行的调查的结果.如果您尚未这样做,我建议从本系列的前两篇文章开始: 问题严重性分析和监视域分析 . 这篇文章着重于故障排除/根本 ...

  6. 《Java性能调优实战》笔记(一)Java编程性能调优、多线程性能优化

    文章目录 一.Java性能调优概述 1.1 性能调优标准 1.2 制定性能调优策略 二.Java编程性能调优 2.1 字符串 2.2 正则表达式 2.3 ArrayList和LinkedList的选择 ...

  7. 11 个简练的 Java 性能调优技巧

    转载自 11 个简练的 Java 性能调优技巧 想要让你的项目一直高性能运作吗?以下有一些技巧你可以拿去消除缓存瓶颈,还有一些其他的性能调优建议. 大多数开发者认为性能优化是一个复杂的话题,它需要大量 ...

  8. java 性能调优_Java性能调优调查结果(第四部分)

    java 性能调优 这是本系列中的最后一篇文章,我们将分析我们在2014年10月进行的Java Performance Tuning Survey的结果.如果您尚未阅读第一篇文章,建议您首先阅读以下内 ...

  9. java 性能调优_Java性能调优调查结果(第二部分)

    java 性能调优 这是系列文章的第二篇,我们将分析2014年10月进行的性能调整调查的结果.如果您尚未阅读第一部分,我们建议从此处开始 . 第二部分将重点监视Java应用程序的性能问题. 特别是,我 ...

最新文章

  1. ITK:获取图像大小
  2. Handler 机制分析
  3. null导入失败_当null检查非常失败时
  4. mysql数据库安全机制研究意义_MySQL数据库的安全机制
  5. 基于redis的分布式锁
  6. gitlab创建分支上传文件_代码管理-gitlab使用方法建议
  7. 对R语言发展与历史的一个初步认识
  8. 联系人排序java代码_Android仿微信联系人按字母排序
  9. 完全掌握1级日本与能力考试语法问题对策
  10. 2. PHP 自动转义函数
  11. mysql 循环插入记录
  12. 如何使用NFC读写器读卡器ACR122U-A9|ACR1251U-M1|ACR1252U读写NDEF格式的智能海报|网址|文本等数据的方法与步骤
  13. 全国计算机软考中级试题,计算机软考中级(网络工程师)历年真题汇总
  14. 为什么计算机没有桌面显示不出来,​为什么电脑图片显示不出来
  15. USER_TAB_COLS,USER_TAB_COLUMNS,ALL_TAB_COLS,ALL_TAB_COLUMNS获取数据库元素的区别
  16. cppm报考条件,看下您符合报考CPPM吗?
  17. Miller_Rabin和Pollard_Rho算法
  18. 做完心脏支架手术后依然心绞痛 、胸闷气短的解决方法
  19. Jupyter notebook中.py与.ipynb文件的import问题
  20. 待定系数法求逻辑代数(函数)最简与或表达式

热门文章

  1. php 根据银行卡号获取所属银行
  2. 计蒜客--弹簧板 DP--动态规划入门
  3. 【C语言】用“*”打印一个三角形
  4. 程序员最恐怖的噩梦是什么?
  5. java中gat和sat方法_2016届高考语法专项复习单项填空(16)
  6. 人机博弈之(一)------博弈介绍
  7. SNR、BER、Eb/N0之间的区别与联系
  8. 人生重开模拟器突然火爆 GitHub,赶紧来玩一玩
  9. 猫眼APP影院静态界面
  10. 读论文Recursive Deep Models for Semantic Compositionality Over a Sentiment Treebank