如何计算一个对象在内存中占多少个字节?

2 个回答

  • 小菜鸟
    二八佳人体似酥,腰间仗剑斩凡夫。
    在分配对象的时候会有一些基本的规则,我们可以根据一些规则大致能判断出来对象大小。 ![对象总述](https://images.codewd.com/76f8f463443c733d67cfb96e2f69abd3.jpg "image.png") 在 Hotspot VM 中,对象在内存中的存储布局分为三个区域: 1. 对象头(Header) 2. 实例数据(Instance Data) 3. 对齐填充(Padding) ### 对象头(Header) 对象头包括以下三部分: 1. **MarkWord**:用于存储对象运行时的数据,例如 HashCode、锁状态标志、GC 分代年龄等。在 64 位操作系统下占 8 字节,32 位操作系统下占 4 字节。 2. **元数据指针**:指向对象的类元数据的指针,虚拟机通过这个指针确定该对象属于哪个类。在开启指针压缩的情况下占 4 字节,未开启时占 8 字节。 3. **数组长度**:仅在数组对象中存在,占 4 字节。非数组对象不包含这一部分。 ### 实例数据(Instance Data) 用于存储对象中的各类字段信息,包括从父类继承而来的字段。 ### 对齐填充(Padding) Java 对象的大小默认按照 8 字节对齐,也就是说 Java 对象的大小必须是 8 字节的倍数。如果对象大小不足 8 字节,则进行填充以满足对齐要求。 ### 8 字节对齐的原因 8 字节对齐并不是浪费空间资源,主要有以下原因: 1. **CPU 内存访问效率**:CPU 进行内存访问时,一次寻址的指针大小是 8 字节,正好与 L1 缓存行的大小一致。如果不进行内存对齐,可能会出现跨缓存行的情况,这会导致缓存行污染,影响性能。 2. **提高内存访问速度**:内存对齐可以提高数据访问的速度,因为对齐的数据可以在一个内存访问周期内读取完毕,而未对齐的数据可能需要多个周期。 通过 8 字节对齐,可以减少 CPU 访问内存时的额外开销,从而提升整体性能。 ![没有字段对齐](https://images.codewd.com/d5d3a920323e3a16057f5537e27d6e45.jpg "image.png") 由于当 obj1 对象的字段被修改后,那么 CPU 在访问 obj2 对象时,必须将其重新加载到缓存行,因此影响了程序执行效率。 也就说,8字节对齐,是为了效率的提高,以空间换时间的一种方案。固然你还能够 16 字节对齐,可是 8 字节是最优选择。 ### Java 对象到底占用多大内存 前面我们分析了 Java 对象到底都包含哪些东西,所以现在我们可以开始剖析一个 Java 对象到底占用多大内存。 由于现在基本都是 64 位的虚拟机,所以后面的讨论都是基于 64 位虚拟机。 首先记住公式,对象由 对象头 + 实例数据 + padding 填充字节组成,虚拟机规范要求对象所占内存必须是 8 的倍数,padding 就是干这个的。 从 JDK 8 开始,HotSpot JVM 默认开启了指针压缩(Compressed Oops)。指针压缩可以减少对象指针的大小,从而节省内存空间,提高内存访问的效率。 ### 一个空的Java object 对象占多大内存? 一个空的对象,在开启压缩指针的情况下,占16个字节。其中Markword占8个字节、类元指针占4个字节, 对齐填充占4个字节。 ### 简单类 ```java public class Test {    private int a;    private long b;    private byte c; } ``` 要占用的空间为: head(8+4) + a(4) + b(8) + c(1) + padding(7) = 32 ### 对象包含基础数据和引用对象 ```java public class Test { byte a; int b; boolean c; long d; Object e; } ``` 要占用的空间为: Object e 存的是地址,占用4字节。 head(8+4) + a(1) + b(4) + c(1) + d(8) + e(4) + padding(2) = 32 通过以上规则和例子,可以帮助我们大致判断Java对象在内存中占用的空间大小。 ### 如何验证估算是否正确 org.openjdk.jol:jol-core:0.9 是一个 Java 库,用于分析和探索 Java 对象布局(JVM 内存布局),帮助理解对象在内存中的实际占用情况。 使用如下: ```js <dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.14</version> </dependency> ``` 示例代码: ```java package com.trace.agent; import org.openjdk.jol.info.ClassLayout; import java.util.List; public class Test { public static void main(String[] args) { ClassLayout classLayout = ClassLayout.parseInstance(new ObjectA()); System.out.println(classLayout.toPrintable()); } } class ObjectA { } ``` 输出如下: ```js com.trace.agent.ObjectA object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) 56 f3 00 jp (01000011 11000001 00000000 11111000) (-13311823) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total ``` 以上代码和输出示例说明了如何使用 JOL 库来验证 Java 对象在内存中的实际占用情况。通过这些工具和方法,可以更准确地分析和理解 Java 对象的内存布局。
    编辑于 2024-06-07 23:43 •IP属地 北京
    举报
    评论
    收藏1

  • 美啦美啦美啦
    不要在赚钱的路上把健康丢了,要在健康的路上顺便把钱赚了。
    由于默认是开启了指针压缩的,现在普遍的机器位数都是64位,如果对象是普通对象,非数组类型,那么就是对象头部分就是12字节(类型指针4字节,Mark Word 8字节),假设这个对象只有一个Integer变量,那么在实例数据部分就是一个引用变量的空间4字节,总共是16字节,由于正好是8的倍数,就不需要进行对齐填充了。
    发布于 2022-02-22 09:39 •IP属地 湖南
    举报
    评论
    收藏