MAT中的Shallow Heap和Retained Heap
概述
在使用MAT分析内存快照的时候我们一般会关注对象占用的内存大小,在MAT中我们可以观察到两个数据Shallow Heap和Retained Heap,网上有很多文章介绍这两个数据的概念以及计算方式,但是很多文章都是有问题的(因此走了不少弯路),所以自己研究了一下,接下来分别介绍。
PS:以下均以32位为例
Shallow Heap
Shallow Heap指的是对象本身占用的内存大小,包含两部分:
- 对象头:可参考Java的对象头和对象组成详解,总结一下就是普通对象的对象头占8字节,数组对象占12字节(包括4字节的数组长度)
- 成员变量:如果是基本类型则按照基本类型的大小来算(如int占用4字节,char占用2字节),如果是对象引用则一律占用4个字节
由以上可得出Shallow Heap计算方法为
1 | Shallow Heap = sizeof(对象头) + sizeof(所有成员变量) |
由于对象内存分配是以8字节为单位的,所以最终的Shallow Heap如果不是8的倍数则需要增加至8的倍数
Retained Heap
为了能够更好的理解,引入Retained Set 和 Dominator的概念
- Dominator:如果GCRoot到对象B的所有路径中均包含对象A,则称A为B的Dominator,意味着如果A被回收,GCRoot将没有到B的链路,B成为可回收对象。
- Retained Set:对象A的Retained Set指的是包含所有以A为Dominator的对象的集合
A的Retained Heap就是A和Retained Set中所有对象的Shallow Heap的合,可以理解为A被回收后进行一次GC,包括A可总共回收的内存就是Retained Heap。
Retained Heap计算方法为
1 | Retained Heap = shallowHeapOf(A) + shallowHeapOf(Retained Set内所有对象) |
举例
接下来举一个例子来应用上述计算方法
1 | public class A { |
内存中有一个对象A,A持有对象B,我们首先来计算A、B的Shallow Heap
计算Shallow Heap
A持有两个基本类型的变量i、l和三个对象引用(数组也是对象!),所以
Shallow Heap = 8(对象头) + 4(int) + 8(long) + 4(对象引用) + 4(对象引用) + 4(对象引用) = 32
B就只有两个基本类型的变量,所以
Shallow Heap = 8(对象头) + 4(int) + 8(long) = 20
但是20不是8的倍数,所以真正的Shallow Heap为24
计算Retained Heap
由于B没有持有任何对象引用,所以Retained Heap就是Shallow Heap,为24
A持有三个对象引用,所以
Retained Heap(A) = Shallow Heap(A) + 三个对象的Retained Heap
intArray不持有对象,只持有基本类型int,所以
Retained Heap(intArray) = Shallow Heap(intArray) = 12(对象头) + 4(int) * 2 = 20 -> 24
charArray同样不持有对象,只持有基本类型char,所以
Retained Heap(charArray) = Shallow Heap(charArray) = 12(对象头) + 2(char) * 2 = 16
综上
Retained Heap(A) = 32(Shallow Heap) + 24(int[]) + 16(char[]) + 24(b) = 96
参考文章
Shallow and retained heap
Java的对象头和对象组成详解
Immediate Dominators