概述
大家在用jvm诊断工具,比如Eclipse MAT或者 Jprofiler分析堆内存的时候,都会看到一些概念或者关键词,如Shallow Heap, Retained Heap, Dominator Tree等,你们知道他们是干嘛的吗?
Eclipse MAT相关截图:
jprofile相关截图:
虽然上面两个工具有点差异,但是他们表达的都是同一个意思。
浅堆(Shallow Heap、Shallow Size)
浅堆是指一个对象所消耗的内存。在32位系统中,一个对象引用会占据4个字节,一个int类型会占据4个字节,long型变量会占据8个字节,每个对象头需要占用8个字节。根据堆快照格式不同,对象的大小可能会同8字节进行对齐。
以jdk7中String为例:2个int值共占8字节,对象引用占用4字节,对象头8字节,合计20字节,向8字节对齐,故占24字节。
int | hash32 | 0 |
int | hash | 0 |
ref | value | alvinalvinalvina |
这24字节为String对象的浅堆大小。它与String的value实际取值无关,无论字符串长度如何,浅堆大小始终是24字节。
深堆(Retained Heap、Retained Size)
这里引入一个保留集(Retained Set)的概念,什么是保留集呢? 当对象A被垃圾回收后,可以被释放的所有的对象集合(包括对象A本身),即对象A的保留集可以被认为是只能通过对象A被直接或间接访问到的所有对象的集合。通俗地说,就是指仅被对象A所持有的对象的集合。
深堆是指对象的保留集中所有的对象的浅堆大小之和,也就是从堆中移除这个实例将释放的内存量。
注意: 浅堆指对象本身占用的内存,不包括其内部引用对象的大小。一个对象的深堆指只能通过该对象访问到的(直接或间接)所有对象的浅堆之和,即对象被回收后,可以释放的真实空间。
举个例子:对象A引用了C和D,对象B引用了C和E。对象A的浅堆大小只是A本身,A的深堆大小为A与D之和,因为对象C还可以被对象B访问,所以不在对象A的深堆范围之内。那A的实际大小是多大呢?在日常开发中,A对象的实际大小是A,C,D相加的大小。 所以说,浅堆、深堆、实际大小都是不一样的。
支配树(Dominator Tree)
支配树可以用来显示堆转储中最大的对象。树的下一层列出了如果删除对父节点的所有传入引用将被垃圾回收的对象。
支配树是一个强大的工具,用于研究哪些对象保持哪些对象存活。同样,可以根据类装入器(例如组件)和包对树进行分组,以简化分析。
那究竟什么是支配树呢?
在对象引用图中,所有指向对象B的路径都经过对象A,则认为对象A支配对象B。如果对象A是离对象B最近的一个支配对象,则认为对象A为对象B的直接支配者。支配树是基于对象间的引用图所建立的。
举个例子:
左图表示对象引用图,右图表示左图所对应的支配树。
- 对象A和B由根对象直接支配,由于在到对象C的路径中,可以经过A,也可以经过B,因此对象C的直接支配者也是根对象。
- 对象F与对象D相互引用,因为到对象F的所有路径必然经过对象D,因此,对象D是对象F的直接支配者。
- 而到对象D的所有路径中,必然经过对象C,即使是从对象F到对象D的引用,从根节点出发,也是经过对象C的,所以,对象D的直接支配者为对象C。
- 同理,对象E支配对象G。到达对象H的可以通过对象D,也可以通过对象E,因此对象D和E都不能支配对象H。
- 经过对象C既可以到达D也可以到达E,因此对象C为对象H的直接支配者。
总结
本文总结了jvm诊断工具比如jprofiler、eclipse mat等一些共通的概念,希望对大家有帮助。