Java对象大小:size和retained size

时间:2023-03-08 23:09:24
Java对象大小:size和retained size

最近看到网上很多文章讲如何计算java对象的大小(size),很多观点不敢苟同。

这是其中一篇比较靠前的文章,写的也比较全面:

http://blog.****.net/iter_zc/article/details/41822719/

认真拜读了一下,有些收获,也有一些疑问。

按照“字节对齐”的理论,所有java对象的大小应该是8的整数倍,且对象头会有8+4=12个字节

下面写了两个类进行验证:

Java对象大小:size和retained size

代码中SizeOfAgent是文中讲到的通过Instrumentation.getObjectSize()计算对象大小的方法

两个对象大小确实是8的倍数,其中b.size的运行结果跟其文中讲到是一样的

b.size = 8(_mark) + 4(oop指针) + 4(i1) + 4(i2) + 1(b1) + 1(b2) + 1(b3) + 1(padding) +  4(str) + 4(obj) = 32

但a.size就不对了,按照该理论

a.size= 8(_mark) + 4(oop指针) + 4(a) + 4(strB) + 4(padding) = 24

但实际运行结果是16,带着疑惑去看了一下官方文档:

Java对象大小:size和retained size

可以看到,Instrumentation.getObjectSize()返回的是一个估算值。

我认为,既然是估算值,对于学术研究,没什么参考性。其实Java对象大小从概念开始就比较复杂,要不然的话,干脆提供c++那样的sizeOf用就行了,还用整个javaagent来包装Instrumentation来提供

Java对象大小应该从两方面去理解:(1)其本身的大小-size (2)其占用堆的大小-retained

用JVisualVm提供的Heap dump可以来看这两个数值,以下是这两个对象在JVisualVm显示的size

Java对象大小:size和retained size

没找到理论依据,我估计是这么计算的

a.size = 8(对象头) + 4(a) + 4(strB) = 16

b.size = 8(对象头) + 4(str) + 4(i1) + 1(b1) + 1(b2) + 4(i2) + 4(obj)  + 1(b3)  = 27

这个算法总结为 size(object) = 8 + sum(sizeField(x)), x为object所有字段

(1)8是对象头,具体是什么,没研究过

(2)sizeField对于简单类型的字段(byte/short/int/long...),返回(1/2/4/8...),对于对象类型的字段,返回4(可以理解为一个指针地址的大小)

读者可以写些其他结构来验证这个公式

那么retained又怎么算呢,首先看下这个单词的解释

The retained size for an object is the quantity of memory this objects preserves from garbage collection

retained表示一个对象无法被垃圾回收的内存大小,反过来讲就是它被回收后,能释多少内存

同一种类型的对象,其size必然是相同的,但retained size不一定相同

这个很好理解,size只跟类型相关,retained size 跟数据相关,一个对象里面有个String字段是null,另外一个对象里面该字段存的“abc”,内存占用必然不相同

注意:不能简单地认为retained size就是对象本身的size+所有字段的retained size,因为对象里面的一个字段可能被其他对象在引用。

下面来看一段代码

Java对象大小:size和retained size

申明了两个类A和B,各有两个对象,通过JVisualVm来看他们的Size和Retained Size(中文版里面叫保留的大小,吐槽一下JDK自带的工具的中文翻译很稀烂):

Java对象大小:size和retained size

Java对象大小:size和retained size

Java对象大小:size和retained size

结论:

  • size(object) = 8 + sum(sizeField(x)); x为object所有字段
  • sizeField(field) = isPrimitive ? primitiveSize(field) : 4;
  • retained(object)= size(object) + sum(retained(x)) ; if x仅被object引用

相关文章