在 Java 中,对象的内存消耗是多少?

2022-08-31 05:43:44

具有 100 个属性的一个对象所占用的内存空间是否与 100 个对象消耗的内存空间相同,每个对象具有一个属性?

为对象分配了多少内存?
添加属性时使用了多少额外空间?


答案 1

Mindprod指出,这不是一个直截了当的问题:

JVM可以自由地在内部以任何它喜欢的方式存储数据,无论字节序或小端序,只要有任意数量的填充或开销,尽管原语的行为必须像它们具有官方大小一样。
例如,JVM 或本机编译器可能决定将 a 存储在 64 位长块中,如 .它不必告诉你,只要程序给出相同的答案。boolean[]BitSet

  • 它可能会在堆栈上分配一些临时对象。
  • 它可能会优化某些变量或方法调用,使其完全不存在,用常量替换它们。
  • 它可以对方法或循环进行版本控制,即编译一个方法的两个版本,每个版本针对特定情况进行优化,然后预先决定调用哪个版本。

然后,硬件和操作系统当然具有多层缓存,芯片缓存,SRAM缓存,DRAM缓存,普通RAM工作集和磁盘上的后备存储。您的数据可能会在每个缓存级别重复。所有这些复杂性意味着您只能非常粗略地预测RAM消耗。

测量方法

可以使用 Instrumentation.getObjectSize() 来获取对象所消耗的存储的估计值。

要可视化实际的对象布局、封装和引用,可以使用 JOL(Java 对象布局)工具

对象标头和对象引用

在现代 64 位 JDK 中,对象具有 12 字节标头,填充为 8 字节的倍数,因此最小对象大小为 16 个字节。对于 32 位 JVM,开销为 8 个字节,填充为 4 个字节的倍数。(来自Dmitry Spikhalskiy的答案Jayen的答案JavaWorld

通常,引用在 32 位平台上为 4 字节,在 64 位平台上为 ;和 32Gb 以上的 8 个字节 ()。(请参阅压缩对象引用。)-Xmx32G-Xmx32G

因此,64 位 JVM 通常需要多 30-50% 的堆空间。我应该使用 32 位还是 64 位 JVM?,2012 年,JDK 1.7)

盒装类型、数组和字符串

与基元类型(来自 JavaWorld)相比,盒装包装器具有开销:

  • 整数:16 字节的结果比我预期的要差一点,因为一个值只能容纳 4 个额外的字节。与可以将值存储为基元类型相比,使用 a 会花费我 300% 的内存开销intInteger

  • Long:16 字节:显然,堆上的实际对象大小受制于特定 JVM 实现针对特定 CPU 类型的低级内存对齐。它看起来像是 8 个字节的对象开销,加上 8 个字节的实际长整型值。相比之下,有一个未使用的4字节孔,很可能是因为使用JVM I强制对象对齐在8字节的字边界上。LongInteger

其他容器也很昂贵:

  • 多维数组:它提供了另一个惊喜。
    开发人员通常采用数字和科学计算等结构。int[dim1][dim2]

    在数组实例中,每个嵌套数组本身都是 a。每个都会增加通常的 16 字节数组开销。当我不需要三角形或粗糙的数组时,这表示纯开销。当阵列维度差异很大时,影响就会增加。int[dim1][dim2]int[dim2]Object

    例如,实例需要 3600 个字节。与实例使用的 1040 字节(具有相同的容量)相比,3600 字节表示 246% 的开销。在 极端情况下,开销因子几乎是 19!将其与 C/C++情况进行比较,在 C/C++情况下,相同的语法不会增加任何存储开销。int[128][2]int[256]byte[256][1]

  • 字符串:a 的内存增长跟踪其内部字符数组的增长。但是,该类又增加了 24 个字节的开销。StringString

    对于大小不超过 10 个字符的非空,相对于有用负载(每个字符 2 个字节加上长度 4 个字节)增加的开销成本介于 100% 到 400% 之间。String

对准

请考虑以下示例对象

class X {                      // 8 bytes for reference to the class definition
   int a;                      // 4 bytes
   byte b;                     // 1 byte
   Integer c = new Integer();  // 4 bytes for a reference
}

一个幼稚的总和会表明一个实例将使用 17 个字节。但是,由于对齐(也称为填充),JVM 以 8 个字节的倍数分配内存,因此它将分配 24 个字节,而不是 17 个字节。X


答案 2

这取决于架构/jdk。对于现代 JDK 和 64 位体系结构,对象具有 12 字节的标头和 8 个字节的填充 - 因此最小对象大小为 16 个字节。您可以使用名为 Java 对象布局的工具来确定大小并获取有关任何实体的对象布局和内部结构的详细信息,或者通过类引用猜测此信息。我的环境中 Integer 的输出示例:

Running 64-bit HotSpot VM.
Using compressed oop with 3-bit shift.
Using compressed klass with 3-bit shift.
Objects are 8 bytes aligned.
Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

java.lang.Integer object internals:
 OFFSET  SIZE  TYPE DESCRIPTION                    VALUE
      0    12       (object header)                N/A
     12     4   int Integer.value                  N/A
Instance size: 16 bytes (estimated, the sample instance is not available)
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

因此,对于 Integer,实例大小为 16 个字节,因为 4 字节 int 紧随标头之后和填充边界之前压缩到位。

代码示例:

import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.util.VMSupport;

public static void main(String[] args) {
    System.out.println(VMSupport.vmDetails());
    System.out.println(ClassLayout.parseClass(Integer.class).toPrintable());
}

如果您使用 maven,要获取 JOL:

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.3.2</version>
</dependency>