Shallow Size 和 Retained Size

使用过Eclipse MAT分析dump文件的同学,应该都会见过Shallow Heap和Retained Heap这两列,稍加留意会发现Retained Heap列的数值总是大于等于Shallow Heap列的值,那么这两个数值究竟有什么联系和区别呢?

Shallow Size

Shallow Size为当前对象占用的内存大小,不包括它引用的对象。

Retained Size

Retained Size为当前对象的大小 + 当前对象(直接/间接)引用对象的大小。

示例

注: 为方便描述,如下示例使用符号表示对象的Retained Size和Shallow Size。

符号 含义
R(A) A对象的Retained Size
S(A) A对象的Shallow Size

1. 简单引用

GC Roots Object A Object B Object C Object D

GC Roots直接引用A和B两个对象,对象B引用C和D两个对象。

1
2
3
4
5
6
// A、C、D未引用其他对象,shallow size和retained size相等
R(A) = S(A)
R(C) = S(C)
R(D) = S(D)

R(B) = S(B) + R(C) + R(D) = S(B) + S(C) + S(D)

2. 复杂引用

GC Roots Object A Object B Object C Object D

在上一种情况的基础上,GC Roots直接引用了对象D。对象D同时被对象B和GC Roots引用。因为D被根直接引用,在对B回收时不会把D当做垃圾进行回收,所以D不算在B的Retained Size内。

1
2
3
4
5
6
// A、C、D未引用其他对象,shallow size和retained size相等
R(A) = S(A)
R(C) = S(C)
R(D) = S(D)

R(B) = S(B) + R(C) = S(B) + S(C)

实验

分析完理论,我们来做个实验验证下。先看下简单引用的情况

1. 简单引用实验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* VM Args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
*/
public class Test {

static class B {
private C c = new C();
private D d = new D();
}

static class C {
}

static class D {
}

public static void main(String[] args) {
List<B> list = new ArrayList<>();

while (true) {
list.add(new B());
}
}
}

这里我们定义了B、C、D三个类,其中B包含了C和D类型的属性。通过不断地往list中塞入新创建的对象B,使得堆溢出出现OutOfMemory异常。注意执行这段代码时先设置下JVM的参数:**-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError**

使用Eclipse MAT打开生成的dump文件,切换到domaintor_tree界面,查看B、C、D对象实例占用的Shallow Heap和Retained Heap大小

简单引用实验

可以看到C、D实例的Shallow Heap大小和Retained Heap大小相等,均为16

B的Retained Heap大小为56,Shallow Heap大小为24。即 S(B) + S(C) + S(D) = 24 + 16 + 16 = 56 = R(B)

2. 复杂引用实验

我们对以上代码稍作修改,在创建B时通过构造函数传入objectD的引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* VM Args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
*/
public class Test {

static class B {
private C c = new C();
// 通过构造函数传递
private D d;

B(D d) {
this.d = d;
}
}

static class C {
}

static class D {
}

public static void main(String[] args) {
// 创建一个D的实例
D objectD = new D();

List<B> list = new ArrayList<>();
while (true) {
list.add(new B(objectD));
}
}
}

复杂引用实验

查看发现B的Retained Set中已经不包含D的实例了,R(B) = 40 = 24 + 16 = S(B) + S(C)

参考

http://supercharles888.blog.51cto.com/609344/1347144
http://blog.csdn.net/kingzone_2008/article/details/9083327
http://bjyzxxds.iteye.com/blog/1532937