我遇到了一个面试问题:
class Test { int a ; int b; char c; }
这个类的内存对象将采用多少以及实现时的原因:
a)32位计算机
b)64位计算机
我的答案是:
For 32-bit: 4+4+2+8 = 18 bytes For 64-bit: 4+4+2+16 = 26 bytes
由于分配了一些额外的内存,在32位系统中是8个字节,在64位系统中是16个字节,除了正常的对象大小.
你能否对这个陈述作出一些解释.
PS:我也想分享一个我从其他来源得到的答案(不能依赖,想要验证):
在32位pc对象占用比其数据成员定义的实际obj大小多8个字节.....并且在64位pc对象占用比其数据成员定义的实际对象大小多16个字节....现在问题出现了为什么会发生...其背后的原因据我所知是:::在32位pc中,前8个字节由JVM保留用于引用类定义,对齐,超类和子类的空间等.对于64位,它为这些保留16个字节.有一个公式用于计算对象在创建时需要多少堆空间,并计算为.......Shallow Heap Size = [对类定义的引用] +超类字段的空间+实例字段的空间+ [alignment]
你如何计算对象"本身"需要多少内存?显然有一个公式:
Shallow Heap Size = [对类定义的引用] +超类字段的空间+实例字段的空间+ [alignment]
似乎没有太大帮助,是吗?让我们尝试使用以下示例代码应用公式:
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的实例需要多少浅堆大小?假设我们使用的是32位x86架构,让我们开始计算它:
作为一个起点 - Y是X的子类,因此它的大小包括来自超类的"东西".因此,在计算Y的大小之前,我们考虑计算X的浅尺寸.
跳到X上的计算,前8个字节用于引用其类定义.此引用始终存在于所有Java对象中,并由JVM用于定义以下状态的内存布局.它还有三个实例变量 - 一个int,一个Integer和一个byte.这些实例变量需要堆如下:
它应该是一个字节.内存中有1个字节.我们的32位架构中的int需要4个字节.对Integer的引用也需要4个字节.注意,在计算保留堆时,我们还应该考虑包含在Integer对象中的原语的大小,但是由于我们在这里计算浅堆,我们在计算中只使用4个字节的引用大小.那么 - 就是这样吗?X = 8字节的浅堆从引用到类定义+ 1字节(字节)+ 4字节(int)+ 4字节(引用整数)= 17字节?事实上 - 没有.现在发挥作用的是对齐(也称为填充).这意味着JVM以8个字节的倍数分配内存,因此如果我们要创建X的实例,我们将分配24个字节而不是17个字节.
如果你可以关注我们,直到这里,好,但现在我们试图让事情变得更加复杂.我们不是创建X的实例,而是创建Y的实例.这意味着什么 - 我们可以从引用到类定义和对齐中扣除8个字节.它在第一时间可能不是太明显但是 - 你注意到在计算X的浅层大小时我们没有考虑到它也扩展了java.lang.Object,就像所有类都做的那样,即使你没有明确说明它你的源代码?我们不必考虑超类的头大小,因为JVM足够聪明,可以从类定义本身检查它,而不必一直将它复制到对象头中.
对齐也是如此 - 在创建对象时,您只对齐一次,而不是在超类/子类定义的边界.因此我们可以肯定地说,在创建X的子类时,您只会从实例变量继承9个字节.
最后,我们可以跳转到初始任务并开始计算Y的大小.正如我们所看到的,我们已经丢失了9个字节到超类字段.让我们看看当我们实际构造Y的实例时会添加什么.
引用其类定义的Y标头占用8个字节.与以前的相同.Date是对象的引用.4字节.简单.List是对集合的引用.再4个字节.不重要的.因此,除了来自超类的9个字节之外,我们还有来自头部的8个字节,来自两个引用的2×4个字节(列表和日期).Y实例的总浅层大小为25个字节,与32对齐.