Java中32位和64位系统的对象大小差异

Him*_*wal 8 java memory jvm memory-management object

我遇到了一个面试问题:

class Test {
    int a ; 
    int b;
    char c;
}
Run Code Online (Sandbox Code Playgroud)

这个类的内存对象将采用多少以及实现时的原因:

a)32位计算机

b)64位计算机

我的答案是:

For 32-bit: 4+4+2+8 = 18 bytes
For 64-bit: 4+4+2+16 = 26 bytes
Run Code Online (Sandbox Code Playgroud)

由于分配了一些额外的内存,在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;
}
Run Code Online (Sandbox Code Playgroud)

现在,我们努力回答的问题是 - 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对齐.

apa*_*gin 10

取决于JVM.至于HotSpot JVM,正确的答案是:

  • 32位JVM:24字节 = align8(4字节mark_word + 4字节类引用+ 4 + 4 + 2字节字段数据)
  • 64位JVM -XX:+ UseCompressedOops:24字节 = align8(8字节mark_word + 4字节类引用+ 4 + 4 + 2字节字段数据)
  • 64位JVM -XX:-UseCompressedOops:32字节 = align8(8字节mark_word + 8字节类引用+ 4 + 4 + 2字节字段数据)


Ste*_*n C 5

我会回答这样的面试问题如下:

  • 它没有指定......

  • 它可能取决于Java实现(即Java发行版,供应商和目标指令集/体系结构)以及32位与64位.

  • 该对象由具有对象标头(其大小特定于平台)的字段(其对齐是特定于平台的)组成,然后将大小向上舍入为特定于平台的堆对象大小粒度.

  • 更好的方法是测量对象大小(例如,在现代HotSpot JVM上使用TLAB).

  • 虽然对象大小可以变化,的含义intchar没有.A int始终为32位有符号,a char始终为16位无符号.


如果面试官告诉你这个:

对于32位:4 + 4 + 2 + 8 = 18字节

这可能意味着对象头的一个intint一个char加2个32位字.但是,我认为他错了.实际上2应该是4因为我认为字段通常存储为在32位字边界上对齐的4(或8)个字节

对于64位:4 + 4 + 2 + 16 = 26个字节

如上所述,但是有2个64位字的对象标题.我再次认为这不正确.

由于分配了一些额外的内存,在32位系统中是8个字节,在64位系统中是16个字节,除了正常的对象大小.

堆分配的粒度是多个2个字.

但我要强调,这不一定是正确的.当然,官方JVM规范中没有任何内容要求对象像这样表示.


Ale*_*tos 2

这是一个棘手的问题。

如果您考虑可变大小,那么它不会改变。32 位和 64 位都会为您提供 24 个字节。但是,64 位会在内存上加载更多内容,从而消耗更多内存。

请记住,32 和 64 是 CPU 可以处理的数量,而不是可以分配的内存量。

有关参考,请参阅 Red Hat 的以下声明:

9.1. 32 位与 64 位 JVM

讨论性能时经常提出的一个问题是,哪个能提供更好的整体性能:32 位还是 64 位 JVM?看起来,64 位操作系统托管的 64 位 JVM 在现代的 64 位硬件上应该比 32 位 JVM 表现得更好。为了尝试提供有关该主题的一些定量数据,我们使用行业标准工作负载进行了测试。所有测试都在同一系统上运行,具有相同的操作系统、JVM 版本和设置,但有一个例外如下所述。

在此输入图像描述

来源: https: //access.redhat.com/documentation/en-US/JBoss_Enterprise_Application_Platform/5/html/Performance_Tuning_Guide/chap-Performance_Tuning_Guide-Java_Virtual_Machine_Tuning.html