MAT使用的几张图例


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

problem suspect 1

problem suspect 2

problem suspect 3

点击detail 可以看详细






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



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

ps :(ListObjects>with incoming references

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

菜单

show  objects  by class 可以看具体的class




参考文章:

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


文/Gracker(简书作者)
原文链接:http://www.jianshu.com/p/d8e247b1e7b2
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

本页内容版权归属为原作者,如有侵犯您的权益,请通知我们删除。

Hibernate的核心接口和类 - 2016-07-22 18:07:25

Hibernate的核心类和接口一共有6个,分别为: Session、SessionFactory、 Transaction、Query、Criteria和Configuration 。这6个核心和类接口在任何开发中都会用到。通过这些接口,不仅可以对持久化对象进行存取,还能够进行事务控制。下面对这6个核心接口和类分别加以介绍。 Configuration Configuration类的作用是对Hibernate进行配置,以及对它进行启动。在Hibernate的启动过程中,Configuration 类的实
1、概述 在我开始构思这几篇关于“自己动手设计ESB中间件”的文章时,曾有好几次动过放弃的念头。原因倒不是因为对冗长的文章产生了惰性,而是ESB中所涉及到的技术知识和需要突破的设计难点实在是比较多,再冗长的几篇博文甚至无法对它们全部进行概述,另外如果在思路上稍微有一点差池就会误导读者。 一个可以稳定使用的ESB中间件凝聚了一个团队很多参与者的心血,一个人肯定是无法完成这些工作的 。但是笔者思索再三,还是下决心将这这即便文章完成,因为这是对本专题从第19篇文章到第39篇文章中所介绍的知识点的最好的总结。我们
1、Exactly_once简介 Exactly_once语义是Flink的特性之一,那么Flink到底提供了什么层次的Excactly_once?有人说是是每个算子保证只处理一次,有人说是每条数据保证只处理一次。其实理解这个语义并不难,直接在官方文档中就可以看出: 从图中可以看出:Exactly_once是为有状态的计算准备的! 换句话说,没有状态的算子操作(operator),Flink无法也无需保证其只被处理Exactly_once!为什么无需呢?因为即使失败的情况下,无状态的operator(ma
简单工厂模式是指专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。 从图中我们可以清楚的看到,该模式中主要包含下面3种 角色: 工厂(Creator)角色 它是工厂模式的核心,负责实现创建所有势力的内部逻辑。工厂类可以被外界直接调用,创建所需的产品的对象。 抽象(Product)角色 简单工厂模式所创建的所有对象的父类,负责描述所有实例所共有的公共接口。 具体产品(Concrete Product)角色 是该模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。一般来讲是

Mybatis学习第一天 - 2016-07-22 17:07:07

  Mybatis第一天 2      MyBatis介绍 MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。 MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集

IP数据报的分片和组装过程 - 2016-07-22 17:07:07

        一份数据从一个主机通过以太网发送到里一个主机时,是要经过很多层路由转发的。其中过程相对比较的复杂,在这里我们要讨论的是IP在路由中转发时是以怎样的形式转发的和目的主机在接受到这写数据报时又是怎样处理的。        首先我们需要了解的是整个 IP数据报的格式 : IP的转发控制都是由IP数据报的头部决定的。在这里我们就不详细的讨论首部的所有字段,我们就讨论一下个分片有关的总长度字段。        在IP数据报中,总长度是16位的字段,依次数据报的最大长度为2^16-1=65535字节,

Java的纤程库 - Quasar - 2016-07-22 17:07:06

最近遇到的一个问题大概是微服务架构中经常会遇到的一个问题: 服务  A  是我们开发的系统,它的业务需要调用  B  、  C  、  D  等多个服务,这些服务是通过http的访问提供的。 问题是  B  、  C  、  D  这些服务都是第三方提供的,不能保证它们的响应时间,快的话十几毫秒,慢的话甚至1秒多,所以这些服务的Latency比较长。幸运地是这些服务都是集群部署的,容错率和并发支持都比较高,所以不担心它们的并发性能,唯一不爽的就是就是它们的Latency太高了。 系统A会从Client接收

操作系统知识点整理 - 2016-07-22 17:07:03

作业 用户在一次解题或一个事务处理过程中要求计算机系统所做工作的集合。它包括用户程序、所需要的数据及控制命令等。作业是由一系列有序的步骤组成的。 进程 一个程序在一个数据集合上的一次运行过程。所以一个程序在不同数据集合上运行,乃至一个程序在同样数据集合的多次运行都是不同的进程。 线程 线程是进程中的一个实体,是被系统独立调度和执行的基本单位。 进程和线程的区别 进程是程序的一次执行。线程可以理解为进程中执行的一段程序片段。 进程是独立的,这表现在内存空间、上下文环境上;线程运行在进程空间内。一般来讲(不适

Spring MVC Web简单入门实例 - 2016-07-22 17:07:52

本文通过一个简单的用户登录例子带你入门Spring MVC Web开发。 开发环境 1、STS 3.7.3(Spring Tool Suit), 下载 。STS其实是一个包装过的Eclipse,由Spring小组开发的,专门用于Spring项目的开发。老规矩,安装之前先要安装jdk,并配置好环境变量。 2、Tomcat 7, 下载Tomcat 7 。sts已经集成了一个叫Pivotal tc Server的web服务器,不过我们一般都使用Tomcat作为我们的Web服务器。 Tomcat配置 。 创建项目
1 简介 Ubuntu分区方案一般有下面三种: /boot 200M、/ 30G、/home 剩余全部空间、swap 8G / 剩余全部空间、swap 8G / 30G、/home 剩余全部空间、swap 8G 第一种分区方案是为了将/boot独立出来,防止操作系统无法启动,这样分的好处博主没体会到,好像/boot没什么用,而且把磁盘搞得支离破碎的,所以博主一般不用这种分法。 第二种分区方案是懒人方案,或者说新手方案,简单粗暴,对于装系统像家常便饭一样的博主来说,这样分区最快啦,毕竟在实体机上操作分区需要