MAT从入门到精通(一)
Heap Dump中主要包含当生成快照时堆中的java对象和类的信息,主要分为如下几类:
Shallow heap是一个对象本身占用的堆内存大小。一个对象中,每个引用占用8或64位,Integer占用4字节,Long占用8字节等等。
Retained set,对于某个对象X来说,它的Retained set指的是——如果X被垃圾收集器回收了,那么这个集合中的对象都会被回收,同理,如果X没有被垃圾收集器回收,那么这个集合中的对象都不会被回收。
Retained heap,对象X的Retained heap指的时候它的Retained set中的所有对象的Shallow si的和,换句话说,Retained heap指的是对象X的保留内存大小,即由于它的存活导致多大的内存也没有被回收。 因为对于某个类的所有实例计算总的retained heap非常慢,因此mat中使用者需要手动触发计算 。
leading set,对象X可能不止有一个,这些对象统一构成了leading set。如果leading set中的对象都不可达,那么这个leading set对应的retained set中的对象就会被回收。一般有以下几种情况:
在下面这张图中,A和B是gc roots中的节点(方法参数、局部变量,或者调用了wait()、notify()或synchronized()的对象)等等。可以看出,E的存在,会导致G无法被回收,因此E的Retained set是E和G;C的存在,会导致E、D、F、G、H都无法被回收,因此C的Retined set是C、E、D、F、G、H;A和B的存在,会导致C、E、D、F、G、H都无法被回收,因此A和B的Retained set是A、B、C、E、D、F、G、H。
MAT根据堆上的对象引用关系构建了支配树(Dominator Tree),通过支配树可以很方便得识别出哪些对象占用了大量的内存,并可以看到它们之间的依赖关系。
如果在对象图中,从gc root或者x上游的一个节点开始遍历,x是y的必经节点,那么就可以说x支配了y(dominate)。
如果在对象图中,x支配的所有对象中,y的距离最近,那么就可以说x直接支配(immediate dominate)y。
支配树是基于对象的引用关系图建立的,在支配树中每个节点都是它的子节点的直接支配节点。基于支配树可以很清楚得看到对象之间的依赖关系。
现在看个例子,在下面这张图中
java程序性能分析之thread dump和heap dump
jmap -dump:live,format=b,file=heap.dmp <pid>
jhat -port 5000 dump文件
在浏览器中,通过http://localhost:5000/进行访问 页面底部点击 Show heap histogram
使用 VisualVM 进行性能分析及调优
可以将jvm日志导出来,有专门的线下、线上工具帮你分析日志,生成图表。也可以配置tomcat等打开jmx端口,jvisualvm 连接远程 ip 和 jmx 端口也可进行分析。
MAT从入门到精通(二)
概览中的饼图:该饼图用于展示retained size最大的对象。可以看到最大的就是 SyncService 对象
SyncService abtestManagerMap table [1] value featureMatcher cache // guava cache
从SyncService 看下去,占空间最大的是 abTestMangerMap,然后不停的向下,直到一个guava cache 对象,由几个segments 组成(就像ConcurrentHashMap是分段一样),每个segment 有table ,table 有array 可以观察其数量非常大。
又一次线上OOM排查经过
使用VisualVM 可以确认字符串是最多的。发现一个奇怪的现象:“计算保留大小”之后,这些String的保留大小都是0。使用VisualVM 显示最近的垃圾回收根节点,发现都找不到。
点击Histogram,即可看到类的占用。在类上选择”List Objects”,即可看到所有对象。
retained heap 从高到低排列
log.debug(JSON.toJSONString(obj));
,obj 在一些场景下会很大。而虽然日志级别是info,debug 日志不会打印。但按照 log.debug
的实现,是先执行 JSON.toJSONString(obj)
,然后判断debug 日志无需输出,因此还是会频繁的执行 JSON.toJSONString(obj)
heap dump和MAT不仅仅用于排查内存相关的问题,也有助于排查线程相关的问题。
通过上图中的那个按钮,可以查看线程视图,线程视图首先给出了在生成快照那个时刻,JVM中的Java线程对象列表。
在线程视图这个表中,可以看到以下几个信息:线程对象的名字、线程名、线程对象占用的堆内存大小、线程对象的保留堆内存大小、线程的上下文加载器、是否为守护线程。
选中某个线程对象展开,可以看到线程的调用栈和每个栈的局部变量,通过查看线程的调用栈和局部变量的内存大小,可以找到在哪个调用栈里分配了大量的内存。
java程序性能分析之thread dump和heap dump
thread dump文件主要保存的是java应用中各线程在某一时刻的运行的位置,即执行到哪一个类的哪一个方法哪一个行上。thread dump是一个文本文件,打开后可以看到每一个线程的执行栈,以stacktrace的方式显示。通过对thread dump的分析可以得到应用是否“卡”在某一点上,即在某一点运行的时间太长,如数据库查询,长期得不到响应,最终导致系统崩溃。单个的thread dump文件一般来说是没有什么用处的,因为它只是记录了某一个绝对时间点的情况。比较有用的是,线程在一个时间段内的执行情况。
两个thread dump文件在分析时特别有效,困为它可以看出在先后两个时间点上,线程执行的位置,如果发现先后两组数据中同一线程都执行在同一位置,则说明此处可能有问题,因为程序运行是极快的,如果两次均在某一点上,说明这一点的耗时是很大的。通过对这两个文件进行分析,查出原因,进而解决问题。
jstack 2576 > thread.txt
Java内存泄漏分析系列之四:jstack生成的Thread Dump日志线程状态 是一个系列,建议结合java线程的状态转换图 一起看。一个thread dump文件部分示例如下:
"resin-22129" daemon prio=10 tid=0x00007fbe5c34e000 nid=0x4cb1 waiting on condition [0x00007fbe4ff7c000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:315) at com.caucho.env.thread2.ResinThread2.park(ResinThread2.java:196) at com.caucho.env.thread2.ResinThread2.runTasks(ResinThread2.java:147) at com.caucho.env.thread2.ResinThread2.run(ResinThread2.java:118)