我知道如何通过添加三个部分来计算Java对象的内存大小:标题+属性+引用。
我也知道Java数组也是一个对象。
但当我阅读"了解JVM高级功能和最佳实践,第二版"时,它说Java阵列的标题由三部分组成;标记字,类指针和数组长度。
Hotspot 64位JVM中总是24个字节。
但是在32位JVM中,如何计算Java阵列的内存大小?
我希望你们能给我一些示例Java代码,向我展示如何计算对象的内存大小,不限于数组对象。
答案 0 :(得分:1)
你可以使用JOL framework来测试这一点,由所有强大的Aleksey Shipilev撰写。
使用它实际上非常简单,首先让我们定义你关心的布局:
Layouter layout32Bits = new HotSpotLayouter(new X86_32_DataModel());
Layouter layout64Bits = new HotSpotLayouter(new X86_64_DataModel());
Layouter layout64BitsComp = new HotSpotLayouter(new X86_64_COOPS_DataModel());
然后,让我们定义一个数组并查看结果:
int [] ints = new int[1];
System.out.println(ClassLayout.parseInstance(ints, layout32Bits).toPrintable());
System.out.println(ClassLayout.parseInstance(ints, layout64Bits).toPrintable());
System.out.println(ClassLayout.parseInstance(ints, layout64BitsComp).toPrintable());
让我们一次运行。对于32bits VM
:
[I object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 09 00 00 00 (00001001 00000000 00000000 00000000) (9)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 10 0b 40 29 (00010000 00001011 01000000 00101001) (692062992)
12 40 int [I.<elements> N/A
52 12 (loss due to the next object alignment)
Instance size: 64 bytes
Space losses: 0 bytes internal + 12 bytes external = 12 bytes total
所以你得到12 bytes
标题,(4 + 4表示两个标题,加4表示数组大小,它是int
);然后你得到40
个字节,用于数组将保存的10个整数。
接下来是我不完全确定我明白的事情。到目前为止,我们有52
个字节,对象在8 bytes
上对齐,这意味着此52值应舍入为56 bytes
以将其与8对齐。
相反,它说12 bytes loss due to the next object alignment
。我只能猜测可能有两件事,first read the comments here或may be some field is there for internal purpose only。
不打算在这里显示其余的示例输出(你也可以这样做),我会问一个关于我很快就填充的事情的后续问题。
答案 1 :(得分:1)
实际的对象大小是特定于实现的,并且甚至不要求对象在其生命周期内保持所需的大小。
有an article on wiki.openjdk.java.net说明:
对象标题布局
对象标题包含本机大小的标记字,klass字,32位长字(如果对象是数组),32位间隙(如果对齐规则需要),然后为零或更多实例字段,数组元素或元数据字段。 (有趣的琐事:Klass元对象在klass词之后立即包含一个C ++ vtable。)
间隙字段(如果存在)通常可用于存储实例字段。
如果UseCompressedOops为false(并且始终在ILP32系统上),则mark和klass都是本机机器字。对于阵列,差距始终存在于LP64系统上,并且仅存在于ILP32系统上具有64位元素的阵列上。
如果UseCompressedOops为true,则klass为32位。非数组在klass之后立即有一个间隙字段,而数组在klass之后立即存储长度字段。
对象的大小计算“标题+属性+引用”是不正确的。首先,对对象的引用不是指示对象大小的一部分。可以对同一个对象进行任意数量的引用,但这些引用根本不必在堆内存或RAM中,因为优化的代码可以纯粹通过CPU寄存器访问对象。
此外,正如上面的引文中所暗示的,存在对齐规则,这些规则使得字段所需的存储器的计算变得非常重要。如果存在适合类型的字段,则标题中可能存在可用于存储实例字段的间隙。虽然可以安排同一类的字段来最小化填充,但是子类必须与超类的布局一起使用,可能会向其添加更多字段,并且如果它具有拟合类型的字段,则可能仅填充间隙,否则,由于类层次结构可能会有更多的差距。
对于数组,您可以从引用的文章中推导出32位HotSpot表示使用12字节的标头,除非类型为long[]
或double[]
,在这种情况下它将是16字节
对于64位实现,UseCompressedOops
选项(默认情况下为on)允许将64位标记字与32位klass和32位长度组合到总共16字节的标头中。仅当UseCompressedOops
关闭时,标题才为24个字节。