1 下面三个是内存泄漏可能性比较大的地方
problem suspect 1
problem suspect 2
点击detail 可以看详细 如果发现里面有自己工程包里面的重复的大量对象的创建
2
在dominator_tree 可以对象按照 group by package 分类 便于查看那部分代码出问题
如果自己的工程包下面占用了大量内存 可能就是问题所在
问题一般可能出在 占用最多的地方
3
选中一个节点 右键查看with incoming reference 可以看
ps :(ListObjects>with incoming references
菜单
show objects by class 可以看具体的class
4
还有一个技巧
如果不容易查到问题 可以比对该项目 之前正确的一个堆文件和一个出问题的堆文件
对比二者差异 容易发现些问题
比如最近的一个线上的问题 莫名服务不可用
对比正常时候堆文件 发现有问题的堆文件 下图二者的比例比平时各高了6个点
右键选择request 弹出菜单选择show objects by class 发现如图
object 对象都是300 response 对象也一样
之前这个工程那些出问题的堆文件 这里显示都是300对象 我猜测可能和线上 tomcat 配置有关
原来server.xml 配置
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的最大线程阀值,需要设置的大一些
目前调高了 参数
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里面以及被取消 做了调整如下暂时 再看一下结果
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这个工具的主界面,而是导入一个文件之后,显示OverView的界面。
打开经过转换的hprof文件:
如果选择了第一个,则会生成一个报告。这个无大碍。
选择OverView界面:
我们需要关注的是下面的Actions区域
Histogram:列出内存中的对象,对象的个数以及大小
Dominator Tree:列出最大的对象以及其依赖存活的Object (大小是以Retained Heap为标准排序的)
Top Consumers : 通过图形列出最大的object
Duplicate Class:通过MAT自动分析泄漏的原因
一般Histogram和 Dominator Tree是最常用的。
要看懂MAT的列表信息,Shallow heap、Retained Heap、GC Root这几个概念一定要弄懂。
Shallow size就是对象本身占用内存的大小,不包含其引用的对象。
因为不像c++的对象本身可以存放大量内存,java的对象成员都是些引用。真正的内存都在堆上,看起来是一堆原生的byte[], char[], int[],所以我们如果只看对象本身的内存,那么数量都很小。所以我们看到Histogram图是以Shallow size进行排序的,排在第一位第二位的是byte,char 。
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。这是个真正的引用图的概念,
为了更好地理解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可以通过相同的方式计算。
GC发现通过任何reference chain(引用链)无法访问某个对象的时候,该对象即被回收。名词GC Roots正是分析这一过程的起点,例如JVM自己确保了对象的可到达性(那么JVM就是GC Roots),所以GC Roots就是这样在内存中保持对象可到达性的,一旦不可到达,即被回收。通常GC Roots是一个在current thread(当前线程)的call stack(调用栈)上的对象(例如方法参数和局部变量),或者是线程自身或者是system class loader(系统类加载器)加载的类以及native code(本地代码)保留的活动对象。所以GC Roots是分析对象为何还存活于内存中的利器。
Thread OvewView可以查看这个应用的Thread信息:
在Histogram和Domiantor Tree界面,可以选择将结果用另一种Group的方式显示(默认是Group by Object),切换到Group by package,可以更好地查看具体是哪个包里的类占用内存大,也很容易定位到自己的应用程序。
在Histogram或者Domiantor Tree的某一个条目上,右键可以查看其GC Root Path:
这里也要说明一下Java的引用规则:
从最强到最弱,不同的引用(可到达性)级别反映了对象的生命周期。
点击Path To GC Roots --> with all references