您的位置:首页 > 大数据 > 人工智能

retained heap shallow heap 区别及介绍

2016-07-21 14:32 429 查看
原文:http://plumbr.eu/blog/how-much-memory-what-is-retained-heap

我将需要多少内存?当你构建一个解决方案,创建一个数据结构或者选择一个算法时这个问题你可能问过自己或者别人.请问这个图是否适合在我的3G堆上,它包含1,000,000条边并且我使用HashMap存储它?我可以使用标准的集合API来构建我的自定义缓存解决方案或者他们带来的开销太大?

显然,回答这个简单的问题有点复杂.在这篇文章中我们将一窥全豹看这个坑到底有多深.

回答标题中的问题来自于几个部分.首先我们需要了解你是否对shallow heap和retained head的大小有兴趣.
shallow heap比较容易 - 它仅仅是自己本身所占有的堆内存.如果计算它有一些细微差别,但是不在本文的范围内.敬请期待未来相同主题的文章.

retained heap有许多计算方式更有趣.你不会对shallow heap感兴趣,在大多数情况下你的问题又可以说成"如果从内存中移除这个对象,通过垃圾回收会有多少内存被释放".

现在,我们都还记得,所有的Java垃圾回收算法都是以下的逻辑:
1. 有一些对象被GC认为是"重要的".被叫做GC root并且几乎不会被回收.比如,当前运行方法的本地变量和输入参数,程序线程,来自本地代码的引用和类似于"全局"的对象.
2. 任何从GC root引用的对象都被假定为在使用所以不会被GC回收.在Java里一个对象可以通过不同的方式引用其他对象,大多数常规情况是一个对象A被存储为对象B的一个字段.这种情况我们叫做"B引用A".
3. 能从GC root到达的所有对象被标记为"in use"的这个过程一直在重复.
4. 所有未使用的对象都可以被回收.

现在说明如何计算retained heap,让我们按照上述的算法看下接下来的示例对象:



简化下这个例子,我们估算所有的对象O1-O4的shallow heap是1kB.我们开始计算对象的retained heap的大小.
1. O4没有引用其他的对象,所以他的retained heap和他的shallow heap的大小相同是1kB.
2. O3有一个到O4的引用.垃圾回收O3意味着O4适合于垃圾回收的也会被回收所以我们说O3的retained heap是2kB.
3. O2有一个到O3的引用.但是现在需要着重注意的是移除O2指向O3的指针也不会使O3可以被垃圾回收,因为O1仍然对他有一个指针.所以O2的retained heap的大小仅仅是1kB.
4. 在这个图上O1保持着所有的引用,因为如果我们移除O1,这个图上的所有对象会被垃圾回收.所以O1的retained heap是4kB.

在实际练习中是哪种含义?实事上,了解shallow heap和retained heap大小的不同可以用工具去做这个工作就像memory探查和heap dump分析 - 比如如果你不了解如何区分这两种heap的大小会证明不可能去钻研Eclipse MAT的.

原文: http://plumbr.eu/blog/how-much-memory-do-i-need-part-2-what-is-shallow-heap

一个特定的数据结构的大小是多少?
"我可以把所有这些对象放到我的ehCache中吗?"

这篇文章是我们试着回答那些问题系列的第二篇.上一篇文章解释了一个对象的retained heap和shallow heap的不同.在文章中我们也提供了一个例子如何去计算一个数据结构的retained heap.在今天的这篇文章中我们将扩展我们在前面称为"simple"的问题. 换句话说 - 如何去估算一个对象的shallow heap.

在第一篇文章中我们推开一大堆复杂性说明计算shallow heap的大小是很容易的 - 它只包含对象本身占用的堆.但是如何计算这个对象"它自己"需要多少堆内存?显示有一个工式:

Shallow Heap的大小 = [类的引用] + 父类字段的大小 + 实例字体的大小 + [对齐]

似乎没有太大的帮助,是吧?我们使用下面的示例代码去尝试工作:

class X {
int a;
byte b;
java.lang.Integer c = new java.lang.Integer();
}

class Y extends X {
java.util.List d;
java.util.Date e;
}

现在,我们努力回答一下这个问题 - 一个Y实例需要多少shallow heap?我们开始计算它,假定我们在一个32位的系统架构上:

开始 - Y是X的子类,所以它的大小包含了来自父类的"某些东西".因此,在计算Y大小的之前,我们先计算X的shallow heap的大小. 

转入关于X的计算,前8个字节被用作类的引用.这个引用在所有的Java对象中一直存在是被JVM用来定义后续状态的内存布局.它有三个实例变量 - 1个int,1个Integer和1个byte.这些实例变量需要的堆如下: 

1. byte应该是什么.在内存是就是1个字节. 
2. int在32位架构中需要4个字节. 
3. Integer的引用也需要4个字节.注意当计算retained heap的时候,我们也会考虑包装到Integer对象中的原始类型的大小,但是我们在这计算shallow heap的大小,我们只需要4个字节的引用. 

所以 - 是这样吗?X的shallow heap是类的引用8个字节 + 1个字节(byte) + 4个字节(int) + 4个字节(Integer的引用) = 17个字节?实事上 - 不对.现在被称为对齐的(也叫补齐)开始发挥作用了. 意思是JVM分配内存是8字节的倍数,所以我们创建一个X实例是分配24个字节而不是17个字节. 

如果你可以跟着我们一直到这,很好,不过现在我们要试着做一些更加复杂的.我们不是创建X实例,而是创建Y实例.意思是 - 我们可以减掉8个字节的类引用和补齐的值.这个可能不太明显但是 - 你是否有注意到当计算X的shallow heap大小的时候我们没有考虑所有的类都是继承了java.lang.Object如果你没有在你的代码中显式的说明.我们没有考虑父类的header,因为JVM足够聪明去检查来类自己的定义,而不是去复制到对象的header. 

这同样适用于补齐 - 当创建一个对象你只要补齐一次,而不是在父类/子类的边界.所以我们可以说当创建一个X子类你将只继承到来自实例变量的9个字节. 

最后我们回到最初的任务开始计算Y的大小.正好我们看到的,我们已经丢失了父类的9个字节.让我们看一下当我们真实的构造一个Y的实例将会增加些什么. 

1. Y的header参见它的类定义占用的8个字节.和前面的一个相同. 
2. Date是一个对象的引用.4个字节.简单. 
3. List是一个容器的引用.也是4个字节.无关紧要. 

所以除了来自父类的9个字节我们还有来自header的8个字节,来自两个引用(List和Date)的2*4个字节.Y实例的总的shallow heap的大小会是25个字节,补齐到32个字节. 

要使计算容易点,我们已经汇总如下图: 

 1234567891011121314151617181920212223242526272829303132
 AlignAlignAlignAlign
XObjectabc 
YObjectabcde 
有了这些知识你可以做什么?连同计算retained heap大小的能力,你现在拥有终极力量去计算你的数据结构真正需要多少内存.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  内存 jvm