MAT使用的几张图例技巧

版权声明:微信公众号 java架构狮 欢迎转载 请注明出处 https://blog.csdn.net/AlbertFly/article/details/51984882


1 下面三个是内存泄漏可能性比较大的地方 

problem suspect 1

problem suspect 2

点击detail 可以看详细  如果发现里面有自己工程包里面的重复的大量对象的创建







2

在dominator_tree 可以对象按照 group by package 分类 便于查看那部分代码出问题

如果自己的工程包下面占用了大量内存 可能就是问题所在


问题一般可能出在 占用最多的地方



选中一个节点 右键查看with incoming reference 可以看 

ps :(ListObjects>with incoming references

 表示的是 当前查看的对象,被外部应用
ListObjects>with outGoing references
表示的是 当前对象,引用了外部对象

菜单

show  objects  by class 可以看具体的class



4

还有一个技巧

如果不容易查到问题 可以比对该项目 之前正确的一个堆文件和一个出问题的堆文件

对比二者差异 容易发现些问题


比如最近的一个线上的问题 莫名服务不可用

对比正常时候堆文件   发现有问题的堆文件 下图二者的比例比平时各高了6个点





右键选择request  弹出菜单选择show  objects  by class 发现如图



object 对象都是300 response 对象也一样



之前这个工程那些出问题的堆文件 这里显示都是300对象 我猜测可能和线上 tomcat 配置有关

原来server.xml 配置

   <Connector port="8080" protocol="HTTP/1.1" URIEncoding="UTF-8"  
           minSpareThreads="25" 
           maxSpareThreads="75"
           enableLookups="false" 
           disableUploadTimeout="true" 
           connectionTimeout="20000"
           acceptCount="300"   
           maxThreads="300" 
           maxProcessors="1000" 
           minProcessors="5"
           useURIValidationHack="false"
           compression="on" 
           compressionMinSize="2048"
           compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain"
               redirectPort="8443" />

我觉着300可能和maxThreads有关 


maxThreads:Tomcat可创建的最大的线程数,每一个线程处理一个请求;minSpareThreads:最小备用线程数,tomcat启动时的初始化的线程数;maxSpareThreads:最大备用线程数,一旦创建的线程超过这个值,Tomcat就会关闭不再需要的socket线程;所以maxThreads决定了tomcat的最大线程阀值,需要设置的大一些


目前调高了 参数

<Connector port="8080" protocol="HTTP/1.1" URIEncoding="UTF-8"  
           minSpareThreads="50" 
           maxSpareThreads="150"
           enableLookups="false" 
           disableUploadTimeout="true" 
           connectionTimeout="20000"
           acceptCount="600"   
           maxThreads="800" 
           maxProcessors="1000" 
           minProcessors="5"
           useURIValidationHack="false"
           compression="on" 
           compressionMinSize="2048"
           compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain"
               redirectPort="8443" />

看看 后面会不会好 这个问题 (问题描述 没有内存溢出,2-3天线上项目不可用,tomcat 假死状态, pid 在 ,服务不可用)


ps:当时怀疑直接提高 maxThreads 没有用,今天果然又挂了,肯定哪里导致request 对象不断增加

貌似没有被回收的感觉 

今天调整了 tomcat的server.xml的配置

比如上面的  maxSpareThreads="150" 参数在tomcat 7里面以及被取消 做了调整如下暂时 再看一下结果

 <Connector   
port="8080"   
protocol="HTTP/1.1"   
maxTreads="20"
acceptCount="10" 

compression="on"   
compressionMinSize="2048"   
noCompressionUserAgents="gozilla, traviata"   
compressableMimeType="text/html,application/xml,application/json,application/javascript,text/css,text/plain"  
connectionTimeout="20000"  
redirectPort="9443"
disableUploadTimeout="true"  
connectionUploadTimeout="60000"  
URIEncoding="UTF-8"/> 

maxTreads 和acceptCount 参数设置的比较低 有问题可以快速暴漏出来 


参考文章:

http://www.jianshu.com/p/d8e247b1e7b2

http://www.tuicool.com/articles/3yMN7z



以下转载


MAT主界面介绍

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

  1. 打开经过转换的hprof文件:

    open hprof
    open hprof

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

    Leak Suspects
    Leak Suspects
  2. 选择OverView界面:

    System OverView
    System OverView

    我们需要关注的是下面的Actions区域

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

      Histogram
      Histogram
    • Dominator Tree:列出最大的对象以及其依赖存活的Object (大小是以Retained Heap为标准排序的)

    Dominator Tree
    Dominator Tree
    • Top Consumers : 通过图形列出最大的object

      Top Consumers
      Top Consumers
    • Duplicate Class:通过MAT自动分析泄漏的原因

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

MAT中一些概念介绍

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

Shallow heap

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

  • 常规对象(非数组)的Shallow size有其成员变量的数量和类型决定。
  • 数组的shallow size有数组元素的类型(对象类型、基本类型)和数组长度决定

因为不像c++的对象本身可以存放大量内存,java的对象成员都是些引用。真正的内存都在堆上,看起来是一堆原生的byte[], char[], int[],所以我们如果只看对象本身的内存,那么数量都很小。所以我们看到Histogram图是以Shallow size进行排序的,排在第一位第二位的是byte,char 。

Retained Heap

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

这里要说一下的是,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来选择这个组。

为了计算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。这是个真正的引用图的概念,

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

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

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

Paste_Image.png
Paste_Image.png
Paste_Image.png
Paste_Image.png

从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可以通过相同的方式计算。

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中的一些有用的视图

Thread OvewView

Thread OvewView可以查看这个应用的Thread信息:

Thread OvewView
Thread OvewView

Group

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

Group
Group

Path to GC Root

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

Path to GC Root
Path to GC Root

这里也要说明一下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实现。

点击Path To GC Roots --> with all references

Path To GC Roots
Path To GC Roots



展开阅读全文

没有更多推荐了,返回首页