Java中如何去计算一个对象的内存占用?一个对象有哪些部分组成

本文详细介绍了Java对象在HotSpot虚拟机中的内存布局,包括对象头的MarkWord、KlassWord和数组长度,实例数据以及对齐填充。通过示例展示了如何计算一个对象的内存占用,并使用JOL库进行验证。内容涵盖了对象内存的基本概念和计算方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >





以HotSpot为例

对象的组成部分

对象的组成部分主要是三个部分:对象头、实例数据、对齐填充
其中对象头又分为三个部分:MarkWord、指向类的指针、数组长度(如果对象是数组的话,如果不是则没有此部分)

1. 对象头

1.1 MarkWord 固定4或者8个字节

在32位虚拟机中,占4个字节

在这里插入图片描述

在64位虚拟机中,占8个字节
在这里插入图片描述
Mark Work存储的是对象自身运行时的数据,比如:锁标志位;是否偏向锁;GC分代年龄;对象的hashCode;获取到该锁的线程的线程ID;偏向时间戳(Epoch)等等。

1.2 Klass Word 指向类的指针 4个字节

对应JVM位数,如果是64位的那么占用8个字节,如果32位占用4个字节,在开启压缩的情况下可以压缩成8->4个字节。
Java对象存放在堆中,但其类信息存放在方法区中,所以Klass word指向该对象的类信息。

1.3 数组长度array length 固定4或者8个字节

如果当前对象是数组,才有此数组长度字段。
如果jvm是32bit,则数组长度是32bit;如果jvm是64bit,则数组长度是64bit

2.2 实例数据

具体的对象属性内存占用大小。
对象的实例数据就是对象在java代码中的属性(包括父类属性以及自己的属性)和值。

2.3 对齐填充

因为jvm要求Java对象占用内存的大小是8byte的倍数,因此需要把对象的大小补齐至8byte倍数。

计算一个对象的内存占用

       <dependency>
            <groupId>org.openjdk.jol</groupId>
            <artifactId>jol-core</artifactId>
            <version>0.9</version>
        </dependency>
public class WordFilter {
    private long aLong = 0L;
    private long bLong = 0L;
    private long cLong = 0L;
    private byte aByte;

    public static void main(String[] args) {
        System.out.println(ClassLayout.parseInstance(new WordFilter()).toPrintable());
    }

}

对象头部分等于 8+4 = 12个字节
实例数据大小 long (8x3) + byte (1x1)=25字节
由于 37不是8的倍数
对其填充为 40
在这里插入图片描述






如有错误欢迎指正

<think>首先,用户的问题是:“我想估算在Java中存储100万个对象占用内存大小。请问Java中100万对象内存占用估算。” 这是一个关于Java内存估算的问题。 我需要参考提供的引用内容来帮助回答。引用中提到了几个关键点: - 引用[1]:解释了Java程序内存占用的因素,包括堆、类、线程、本地数据结构等。关键公式是:`(-Xmx) + (-XX:MaxPermSize) + 线程数 * (-Xss) + 其它内存`,其中其它内存大约是jvm内存的5%。 - 引用[2]:讨论了Java线程数的估算,涉及堆内存大小、线程栈大小和系统限制。 - 引用[3]:解析Java对象占用内存空间,指出包装类型比原生类型耗更多内存。例如,Integer占用16字节(包括对象头和补齐),而原生int占用4字节。 - 引用[4]:如何查看内存占用大小,包括使用序列化计算对象大小。 - 引用[5]:Java内存优化,建议根据应用需求调整启动参数。 用户的问题是关于存储100万个对象内存占用,所以我应该专注于对象本身的占用,而不是整个程序的内存。 关键点来自引用[3]:Java对象内存中的大小取决于对象头和字段对齐。例如: - 一个对象占用8字节(对象头)。 - 添加字段时,大小增加,但可能因对齐而补齐。 - 包装类型如Integer占用16字节(8字节对象头 + 4字节int值 + 4字节补齐),而原生int占用4字节。 对于用户的问题,我需要估算100万个特定对象占用。但用户没有指定对象类型,所以我应该假设一个通用场景或解释如何估算。 用户说“100万个对象”,但没有说是什么对象。我可以基于常见类型如Integer或自定义类来举例。 步骤: 1. **理解Java对象内存占用**:每个对象在堆上存储时,包括对象头、实例数据和对齐填充。 - 对象头:通常8字节(在32位JVM上)或12-16字节(在64位JVM上),取决于是否启用指针压缩。现代JVM默认启用指针压缩(-XX:+UseCompressedOops),对象头通常12字节(8字节Mark Word + 4字节Klass Pointer)。 - 实例数据:每个字段的大小,如int 4字节,long 8字节,引用4字节(启用指针压缩时)。 - 对齐填充:使对象大小是8字节的倍数。 2. **估算单个对象大小**: - 例如,一个简单的对象如: ```java class MyObject { int id; // 4 bytes String name; // 引用,4 bytes(启用指针压缩) } ``` 对象头:12字节 字段:id 4字节 + name引用4字节 = 8字节 总:12 + 8 = 20字节,但需要对齐到8字节倍数,所以24字节。 - 另一个例子:Integer对象,从引用[3]看,占用16字节。 3. **计算100万个对象**:一旦知道单个对象大小,乘以1,000,000。 4. **考虑额外开销**:对象存储在堆中,但堆还有其他开销,如垃圾收集器数据结构。但用户问题可能只关心对象本身占用,所以专注于对象大小。 5. **影响因素**: - JVM版本和设置:如指针压缩是否启用。 - 对象类型:自定义类或内置类。 - 数组 vs 列表:如果存储在一个数组中,数组本身也有开销。 用户说“存储100万个对象”,可能意味着在集合中,如数组或ArrayList。 来自引用[4],有序列化方法计算对象大小,但序列化大小不一定与堆大小相同。 最佳实践:使用工具如jol (Java Object Layout) 来精确测量。 但既然用户要估算,我可以提供一般方法。 结构化回答: - 解释对象内存组成。 - 举例常见对象。 - 给出估算公式。 - 提醒额外因素。 生成相关问题:作为系统指令,我需要生成相关问题。 参考引用:在回答中自然地添加引用标识。 回答使用中文。 最终估算: 假设一个简单对象,如Integer: 单个Integer占用16字节(根据引用[3])。 100万个Integer:16 bytes * 1,000,000 = 16,000,000 bytes ≈ 16 MB。 但实际中,对象存储在集合中,如ArrayList,ArrayList本身有开销。 ArrayList内部是数组,数组有对象头。 例如,ArrayList with 1,000,000 elements: - Array of Integer: 数组对象头 + 长度字段 + 元素引用。 在64位JVM启用指针压缩: - 数组对象头:12字节(Mark Word + Klass Pointer + array length) - 长度:4字节(int) - 元素:每个是引用,4字节,所以1,000,000 * 4 bytes = 4,000,000 bytes 总数组大小:12 + 4 + 4,000,000 = 4,000,016 bytes ≈ 4 MB 但数组存储的是引用,实际对象是单独的。 所以100万个Integer对象:每个16字节,总16,000,000 bytes ≈ 16 MB 加上ArrayList开销:约4 MB,总约20 MB。 但Integer对象本身是独立的。 对于自定义对象。 例如,用户可能有一个类。 我假设一个典型类。 从引用[3],Long占用16字节,原生long 8字节。 所以对于原生类型数组,更高效。 但用户说“对象”,所以是对象。 在回答中,我应该说明这些。</think>### Java中100万对象内存占用估算 在Java中,估算存储100万个对象内存占用需要考虑对象本身的大小、JVM内存管理机制(如对象头、对齐填充)、以及存储结构(如数组或集合)。内存占用主要取决于对象类型、JVM设置(如是否启用指针压缩)和运行时环境。以下我将逐步解释估算方法,并提供具体示例。估算基于常见64位JVM(默认启用指针压缩 `-XX:+UseCompressedOops`),因为这是现代Java应用的典型配置[^3]。 #### 步骤1: 理解Java对象内存组成 每个Java对象在堆内存占用的空间包括: - **对象头(Object Header)**:存储Mark Word(8字节)和Klass Pointer(启用指针压缩时为4字节),共12字节。 - **实例数据(Instance Data)**:对象的字段大小,如`int`占4字节、`long`占8字节、对象引用(启用指针压缩时)占4字节。 - **对齐填充(Padding)**:JVM要求对象大小是8字节的倍数,不足时会补齐。 - **额外因素**:如果对象存储在集合(如ArrayList)中,还需考虑集合本身的开销(如数组头和引用)。垃圾收集器也可能添加隐藏开销,但通常较小(约5%~10%)[^1]。 单个对象大小公式: `对象大小 ≈ 对象头 + Σ(字段大小) + 补齐` #### 步骤2: 估算单个对象大小(常见示例) 以下是不同对象类型的单个大小估算(基于64位JVM,启用指针压缩): - **Integer对象**:对象头12字节 + `int`值4字节 + 补齐到16字节(因对齐要求)。总大小16字节[^3]。 - **自定义对象示例**:假设一个简单类: ```java class Person { int id; // 4字节 String name; // 引用, 4字节(启用指针压缩) boolean active; // 1字节,但补齐后通常占4字节(因对齐) } ``` - 对象头:12字节。 - 字段:`id`(4字节) + `name`引用(4字节) + `active`(1字节,但补齐后占4字节)。 - 总大小:12 + 4 + 4 + 4 = 24字节(无补齐,已对齐)。 - **原生类型数组**:如`int[]`存储100万个`int`,每个元素4字节,数组头12字节 + 长度字段4字节,元素总大小4 × 1,000,000 = 4,000,000字节。总大小 ≈ 4,000,016字节(~3.81 MB),效率更高,因为无对象头开销[^3][^4]。 #### 步骤3: 计算100万个对象的总内存占用内存占用 = 对象数量 × 单个对象大小 + 集合开销(如适用)。 以常见场景为例: - **场景1: 100万个Integer对象存储在ArrayList中**: - 单个Integer大小:16字节[^3]。 - 对象总大小:1,000,000 × 16字节 = 16,000,000字节(~15.26 MB)。 - ArrayList开销:内部使用数组,数组头12字节 + 长度字段4字节 + 元素引用数组(每个引用4字节)。引用数组大小:1,000,000 × 4字节 = 4,000,000字节(~3.81 MB)。 - 总大小 ≈ 16,000,000 + 4,000,016 = 20,000,016字节(~19.07 MB)。 - **场景2: 100万个自定义Person对象存储在数组中**: - 单个Person大小:24字节(如上例)。 - 对象总大小:1,000,000 × 24字节 = 24,000,000字节(~22.89 MB)。 - 数组开销:数组头12字节 + 长度字段4字节 + 元素引用数组(如存储引用)。引用数组大小:1,000,000 × 4字节 = 4,000,000字节(~3.81 MB)。 - 总大小 ≈ 24,000,000 + 4,000,016 = 28,000,016字节(~26.70 MB)。 - **场景3: 100万个原生int值存储在int数组中(非对象)**: - 每个`int`占4字节,无对象头。 - 数组大小:数组头12字节 + 长度字段4字节 + 元素:1,000,000 × 4字节 = 4,000,016字节(~3.81 MB)。 - 总大小 ≈ 3.81 MB(更高效,推荐用于大规模数据)[^3][^4]。 #### 步骤4: 影响估算的关键因素 - **JVM设置**:禁用指针压缩时,对象头和引用可能翻倍(如对象头16字节,引用8字节),增加内存占用[^1][^3]。 - **实际对象类型**:包装类型(如Integer)比原生类型(如int)占用更多内存(Integer比int多300%内存)。字符串对象更大,因为String内部有`char[]`和字段[^3]。 - **存储结构**:ArrayList比裸数组多一层引用开销;HashMap等集合因哈希表结构占用更多(约额外20%~50%)[^4]。 - **运行时开销**:JVM垃圾收集、线程栈(-Xss)、类元数据(-XX:MaxMetaspaceSize)等会增加总内存,但对象占用是主体。根据经验,总JVM内存对象占用 × 1.5(包括其他因素)[^1][^5]。 - **测量工具**:估算可能有偏差,建议使用工具精确测量: - **jol (Java Object Layout)**:命令行工具,输出对象内存布局。 - **序列化方法**:如引用[4]所述,序列化对象到文件,文件大小近似堆大小(但有误差)。 - **jmap**:运行时分析堆内存(`jmap -histo <pid>`)[^2][^4]。 #### 估算总结 - **基本估算**:存储100万个简单对象(如Integer或Person),内存占用约15–30 MB,具体取决于对象类型和存储方式。 - **优化建议**: - 优先使用原生类型数组(如`int[]`)而非对象数组,以减少内存开销。 - 调整JVM参数:如增大`-Xmx`(堆大小)防止OOM,但需根据对象大小计算(参考公式:`-Xmx ≈ 对象总大小 × 1.5`)[^1][^5]。 - 监控内存:使用`jcmd <pid> GC.heap_info`或VisualVM实时监控[^2]。 例如,100万个Integer对象占用19 MB,但实际应用中,如果包含字符串或复杂结构,可能达到100 MB以上。建议在开发环境用jol验证具体类的大小。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只小小狗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值