前言
快速定位性能故障并非一朝一夕之功,需要我们对很多概念有很深刻的理解,在前文中,我们介绍了heap dump的相关概念和其获取方式,今天我们一起来了解一下什么是:
Shallow 和 retained sizes。
GC ROOT是什么?
在java语言中,都是通过可达性分析来判定对象是否存活的。此算法的基本思路是:通过一系列的称为“GC Roots”的对象作为起点,从这些节点向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连,则证明此对象是不可达的。
在上图右侧中,我们可以看到,对象5/6/7虽然有依赖关联,但是他们到GC ROOT根节点是不可达的,所以这三个节点对象会被判定为是可回收的。
GC ROOT的定义比较特别,他们不属归属于对象图中,对象也不能反向的依赖他们,这也确保了不会出现循环引用的问题。因此也容易得出,只有引用类型的变量才被认为是Roots,值类型的变量永远不被认为是Roots。
在Java中,可作为GC Roots的对象包括以下几种:
虚拟机栈(栈帧中的局部变量表,Local Variable Table)中引用的对象。
方法区中类静态属性引用的对象。
方法区中常量引用的对象。
本地方法栈中JNI(即一般说的Native方法)引用的对象。
看到这里你可能要问,选择这些对象的依据是什么呢?
首先,GCROOT的目标对象是要以当前还在存活的对象集合,因此必须要选取确定存活的引用类型对象,GC管理的区域是java的堆,虚拟机栈、方法区和本地方法栈不被GC所管理,因此选用这些区域内引用的对象作为GC Roots,是不会被GC回收的。
其中虚拟机栈和本地方法栈都是线程私有的内存区域,只要线程没有终止,就能确保它们中引用的对象的存活。而方法区中类静态属性引用的对象是显然存活的。常量引用的对象在当前可能存活,因此,也可能是GC roots的一部分。
下图是使用MAT工具中的 “path to GC roots ”功能分析出来的引用链。
它标识从当前对象到GC roots的路径,这个路径解释了为什么当前对象还能存活,对分析内存泄露很有帮助。
在查询到GC root的路径时,默认是包含所有引用的,从GC角度说,一个对象无法被GC,一定是因为有强引用存在,其它引用类型在GC需要的情况下都是可以被GC掉的,所以这里我使用 exclude all phantom/weak/soft etc. references 只查看GC路径上的强引用。
shallow heap和retained heap
直译过来是浅层堆和保留堆的意思。先说一说其基本的概念。
shallow heap
表示对象本身占用内存的大小,也就是对象头加成员变量(不是成员变量的值)的总和。
如一个引用占用32或64bit,一个integer占4bytes,Long占8bytes等。
如简单的一个类里面只有一个成员变量int i,那么这个类的shallow size是12字节,因为对象头是8字节,成员变量int是4字节。
常规对象(非数组)的Shallow size有其成员变量的数量和类型决定,数组的shallow size有数组元素的类型(对象类型、基本类型)和数组长度决定。
对象的值是分配给存储对象本身的内存量,不考虑所引用的对象。常规(非数组)对象的浅大小取决于其字段的数量和类型。数组的浅尺寸取决于数组的长度及其元素(对象、基本类型)的类型。一组对象的浅尺寸表示该集合中所有对象的浅尺寸之和。
retained heap
如果一个对象被释放掉,那会因为该对象的释放而减少引用进而被释放的所有的对象(包括被递归释放的)所占用的heap大小,即对象被垃圾回收器回收后能被GC从内存中移除的所有对象之和。相对于shallow heap,Retained heap可以更精确的反映一个对象实际占用的大小(若该对象释放,retained heap都可以被释放)。
实际案例分析
正如上图所示:
在这两张图中,我们画出了GC ROOT到所有对象引用链。在这里我们着重分析一下Retained size。
对于obj1这个对象:
GC ROOT指向它,并且它依赖于obj2、obj3、obj4,但是由于obj3同样也被GC ROOT所指。
所以:
分析obj1:
对于图1,retained size包括:obj1+obj2+obj4
对于图2,retained size包括:obj1+obj2+obj3+obj4
分析obj2:
对于图1:retained size包括:obj2+obj4
对于图2:retained size包括:obj2+obj3+obj4
总结
本篇文章围绕内存分析中的Shallow 和Retained heap扩展了解了几个知识点如下:
- gc root的定义和概念的了解。
- 对象可达性分析和选择gc root的依据。
- 针对单个对象使用“path to GC roots”查看其引用树。
- shallow heap和retained heap的基本概念。
创建了一个java方面的互助群,和其他传统的学习群不同。
在本群,你可以
1)阐述你在开发过程中遇到的问题,群友集思广益,高效解答。
2)分享自己学习的一些心得,让后来学习者少踩坑。
3)资源共享,无论是好的学习视频还是文档都可以在群内共享。
别人有可能可以给你提供一些思路和看法
同样,如果你也乐于帮助别人,那解决别人遇到的问题,也同样对你是一种锻炼。