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 RootsObject AObject BObject CObject 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 RootsObject AObject BObject CObject 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