转载

mat使用

前言

MAT从入门到精通(一)

基本概念

heap dump

Heap Dump中主要包含当生成快照时堆中的java对象和类的信息,主要分为如下几类:

  1. 对象信息:类名、属性、基础类型和引用类型
  2. 类信息:类加载器、类名称、超类、静态属性
  3. gc roots:JVM中的一个定义,进行垃圾收集时,要遍历可达对象的起点节点的集合
  4. 线程栈和局部变量:快照生成时候的线程调用栈,和每个栈上的局部变量 Heap Dump中没有包含对象的分配信息,因此它不能用来分析这种问题:一个对象什么时候被创建、一个对象时被谁创建的。

Shallow vs. Retained Heap

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中的对象就会被回收。一般有以下几种情况:

  1. 某个类的所有实例对象,这个类对象就是leading object
  2. 某个类记载器加载的所有类,以及这些类的实例对象,这个类加载器对象就是leading object
  3. 一组对象,要达到其他对象的必经路径上的对象,就是leading object

在下面这张图中,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

MAT根据堆上的对象引用关系构建了支配树(Dominator Tree),通过支配树可以很方便得识别出哪些对象占用了大量的内存,并可以看到它们之间的依赖关系。

如果在对象图中,从gc root或者x上游的一个节点开始遍历,x是y的必经节点,那么就可以说x支配了y(dominate)。

如果在对象图中,x支配的所有对象中,y的距离最近,那么就可以说x直接支配(immediate dominate)y。

支配树是基于对象的引用关系图建立的,在支配树中每个节点都是它的子节点的直接支配节点。基于支配树可以很清楚得看到对象之间的依赖关系。

现在看个例子,在下面这张图中

  1. x节点的子树就是所有被x支配的节点集合,也正式x的retained set;
  2. 如果x是y的直接支配节点,那么x的支配节点也可以支配y
  3. 支配树中的边跟对象引用图中的引用关系并不是一一对应的。

mat使用

生成dump 文件

java程序性能分析之thread dump和heap dump

jmap -dump:live,format=b,file=heap.dmp <pid>

dump文件分析工具

jhat

jhat -port 5000 dump文件 在浏览器中,通过http://localhost:5000/进行访问 页面底部点击 Show heap histogram

使用 VisualVM 进行性能分析及调优

使用 VisualVM 进行性能分析及调优

可以将jvm日志导出来,有专门的线下、线上工具帮你分析日志,生成图表。也可以配置tomcat等打开jmx端口,jvisualvm 连接远程 ip 和 jmx 端口也可进行分析。

mat

MAT从入门到精通(二)

mat使用

  1. overview中的饼图:该饼图用于展示retained size最大的对象
  2. 常用的分析动作:类直方图、支配树、按照类和包路径获取消耗资源最多的对象、重名类。
  3. 报告相关:Leak Suspects用于查找内存泄漏问题,以及系统概览
  4. Components Report:这个功能是一组功能的集合,用于分析某一类性的类的实例的问题,例如分析java.util.*开头的类的实例对象的一些使用情况,例如:重复字符串、空集合、集合的使用率、软引用的统计、finalizer的统计、Map集合的碰撞率等等。
  5. inspector窗口的下半部分是展示类的静态属性和值、对象的实例属性和值、对象所属的类的继承结构。

fullgc 排查实战

某个对象数量太多

mat使用

概览中的饼图:该饼图用于展示retained size最大的对象。可以看到最大的就是 SyncService 对象

mat使用

SyncService
    abtestManagerMap
        table
            [1]
                value
                    featureMatcher
                        cache // guava cache

从SyncService 看下去,占空间最大的是 abTestMangerMap,然后不停的向下,直到一个guava cache 对象,由几个segments 组成(就像ConcurrentHashMap是分段一样),每个segment 有table ,table 有array 可以观察其数量非常大。

yonnggc 太多进而导致 fullgc太多

又一次线上OOM排查经过

  1. 使用VisualVM 可以确认字符串是最多的。发现一个奇怪的现象:“计算保留大小”之后,这些String的保留大小都是0。使用VisualVM 显示最近的垃圾回收根节点,发现都找不到。

    mat使用

  2. 使用MAT 寻找内存较大的对象。但意外发现size 只有4xxM。
  3. MAT的主要目标是排查内存占用量,所以默认大小是不计算不可达对象的。在”Preferences=>Memory Analyzer”中勾选”Keep Unreachable Objects”,关闭mat,删除索引文件Dump同路径下的所有”.index”文件,启动mat,即可看到所有的对象。
  4. overview 发现size 有1GB
  5. 保留大小是什么呢?它是分析工具从GC roots开始查找,找到的所有不会回收的对象,然后按照引用关系,计算出这个“对象以及它引用的对象”的内存大小。结合以上现象:这些大String是临时对象,没有什么对象持有它——通过分析这些String的依赖关系也说明了这一点。 这些对象是可以被回收的,换句话说,并不是有明显的内存泄露。只是对象大 + 写入太快 导致了频繁的younggc,younggc 忙不过来年轻代满了 新的String 就进入老年代,然后引发fullgc
  6. 这些字符串是什么呢?
  7. 点击Histogram,即可看到类的占用。在类上选择”List Objects”,即可看到所有对象。

    mat使用

    retained heap 从高到低排列

  8. 在对象上选择”Copy=>Value to File”,即可保存到文件。查看文件内容 是一个对象json 后的内容
  9. 在代码中有大量的 log.debug(JSON.toJSONString(obj)); ,obj 在一些场景下会很大。而虽然日志级别是info,debug 日志不会打印。但按照 log.debug 的实现,是先执行 JSON.toJSONString(obj) ,然后判断debug 日志无需输出,因此还是会频繁的执行 JSON.toJSONString(obj)
  10. 解决这个问题参见log4j学习

mat 线程视图

heap dump和MAT不仅仅用于排查内存相关的问题,也有助于排查线程相关的问题。

mat使用

通过上图中的那个按钮,可以查看线程视图,线程视图首先给出了在生成快照那个时刻,JVM中的Java线程对象列表。

mat使用

在线程视图这个表中,可以看到以下几个信息:线程对象的名字、线程名、线程对象占用的堆内存大小、线程对象的保留堆内存大小、线程的上下文加载器、是否为守护线程。

选中某个线程对象展开,可以看到线程的调用栈和每个栈的局部变量,通过查看线程的调用栈和局部变量的内存大小,可以找到在哪个调用栈里分配了大量的内存。

jstack thread dump

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)
原文  http://qiankunli.github.io/2020/03/25/mat_intro.html
正文到此结束
Loading...