【发布时间】:2014-07-19 04:48:10
【问题描述】:
我遇到了一个面试问题:
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 字节,除了对象的正常大小。
请您对此声明做一些解释。
P.S.:我也想分享一个我从另一个来源得到的答案(不能依赖,想验证):
在 32 位 pc 对象中,比其数据成员定义的实际 obj 大小多 8 个字节.....而在 64 位 pc 对象中,比其数据成员定义的实际对象大小多 16 个字节.. ..现在问题出现了它为什么会发生......据我所知,其背后的原因是 ::: 在 32 位 pc 中,前 8 个字节由 JVM 保留用于引用类定义、对齐、超类和子类的空间类等..对于64位,它为这些保留16个字节..有一个公式可以计算对象在创建时需要多少堆空间,计算方式为...... .浅堆大小 = [引用类定义] + 超类字段空间 + 实例字段空间 + [对齐]
如何计算对象“本身”需要多少内存?显然有一个公式:
Shallow Heap Size = [对类定义的引用] + 超类字段的空间 + 实例字段的空间 + [对齐]
似乎没有太大帮助,是吗?让我们尝试使用以下示例代码应用公式:
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 对齐。
【问题讨论】:
-
我想答案应该是“未定义”。抛开这个作为面试题的无用,面试官是否指定了某个 JVM 实现?
-
@MarkPeters 我在网上看到了这个问题,所以我想在这里确认一下。
-
显然这是一个荒谬的问题,因为(除其他原因外)不同的“64 位计算机”具有不同的处理器。我的理解是,基于 64 位 80x86/Pentium 的处理器仍然具有其 32 位表亲所具有的所有 32 位(和 16 位指令),因此没有内在的理由占用 64 位来存储 32位整数。除非它以其他方式以某种方式简化了实现。
-
已编辑。从其他来源添加了答案,但确实想弄清楚。
-
如果你在采访中被问到这个问题,不要说“它是未定义的”。任何人都可以这么说——它对你没有任何积极的意义。尝试回答并解释您对标题、字长、体系结构、内存对齐等的了解。如果必须在最后确定答案。
标签: java object memory memory-management jvm