jvm之对象大小分析

news/2025/2/14 3:10:27/

写在前面

本文看下计算对象大小相关内容。

1:基础内容

1.1:对象的结构

一个对象由对象头和对象体组成,其中对象头包含如下内容:

标记字(mark word):存放GC年龄信息,对象锁信息等,占用8个字节
class指针:指向方法区中的class文件信息,占用4个字节(指针压缩)
数组长度信息(array length):数组特有,记录数组的长度,int表示,所以是8个字节,该项非数组可忽略

对象体存储的是具体的对象内容以及内部padding:

对象内容:具体的属性信息
内部padding:如果不是8字节的整数倍,则填充为8字节的整数倍,因为CPU是以8byte为单位来获取数据的

当对象头和对象体的总大小不是8byte的整数倍时需要通过外部aligment来填充到整数倍,整体结构如下:

在这里插入图片描述

因此就算是一个空对象其大小也至少是标记字8字节+class指针4字节+对象内容0字节+外部alignment4字节=16字节。如下定义一个类:

public class EmptyObject {
}

然后创建100个对象实例,如下:

public class Main {public static void main(String[] args) throws InterruptedException {List<EmptyObject> oneHundredList = new ArrayList<>();for (int i = 0; i < 100; i++) {oneHundredList.add(new EmptyObject());}Thread.sleep(Integer.MAX_VALUE);}
}

如下查看:

C:\Users\Administrator>jps -l
7984 org.jetbrains.jps.cmdline.Launcher
12692
20196 sun.tools.jps.Jps
3512 dongshi.daddy.objectsize.Main
14892
19580 org.jetbrains.idea.maven.server.RemoteMavenServer36
C:\Users\Administrator>jmap -histo 3512 > d:\\test\\emptyobj100.txt

在这里插入图片描述

可以看到每个是16字节,当我们定义一个int属性,大小是多少呢?依然是16字节,因为有了4字节的int就不要4字节的外部alignment了,如下:

public class EmptyObject {int num;
}

重复执行创建100个对象实例的程序:

C:\Users\Administrator>jps -l
11780 sun.tools.jps.Jps
12692
12568 dongshi.daddy.objectsize.Main
14892
19356 org.jetbrains.jps.cmdline.Launcher
19580 org.jetbrains.idea.maven.server.RemoteMavenServer36C:\Users\Administrator>jmap -histo 12568 > d:\\test\\withint.txt

在这里插入图片描述

那如果我们再添加一个short成员变量,此时一个EmptyObject占用多大内存呢?答案是24字节,计算如下:

对象头标记字:8字节
对象头class指针:4字节对象内容int:4字节
对象内容short:2字节
内部padding:2字节此时总共20字节,不是8整倍数,所以需要外部alignment4字节所以总共是24字节

修改如下:

public class EmptyObject {int num;short shortNum;
}

重新生成100个对象实例后查看:

C:\Users\Administrator>jmap -histo 3952 | findstr ":EmptyObject"C:\Users\Administrator>jmap -histo 3952 | findstr "EmptyObject"28:           100           2400  dongshi.daddy.objectsize.EmptyObject

可以看到确实是24字节。

2:包装类型和原生类型

先说结论,包装数据类型要比原生类型占用更多的内存,因为多了对象头和填充的内存占用,分别来看下Integer和Long。

2.1:Integer

对于int,其占用4个字节,但是如果是其包装类型Integer,则要占用(对象头标记字8字节+对象头class指针4字节+对象内容int本身4字节=16字节),如下也可以验证:

C:\Users\Administrator>jmap -histo 3952 | findstr "java.lang.Integer"19:           258           4128  java.lang.Integer

总大小4128除以对象个数258等于16字节。

2.2:Long

对于long,其占用8个字节,但是如果是其包装类型Long,则要占用(对象头标记字8字节+对象头class指针4字节+对象内容int本身8字节+外部alignment4字节=24字节),如下也可以验证:

C:\Users\Administrator>jmap -histo 7212 | findstr "java.lang.Long"28:           100           2400  java.lang.Long

可以看到每个的大小是2400/100=24字节

所以在实际编码中,能使用基本数据类型的还是使用基本数据类型,因为包装数据类型的内存占用量相比于基础数据类型要多出几倍。

3:数组

以int数组为例,对于一维的int数组,其结构如下:

在这里插入图片描述

因此如果是int[256]则大小是(标记字8字节+class指针4字节+数组长度4字节+内容256*4=1040字节)

对于二维数组int[dim1][dim2]而言,每个int[dim2]都是使用一个额外的对象来表示的,因此会占用更大的内存空间,如果我们将int[256]使用int[128][2]来表示的话,则结构如下图:

在这里插入图片描述

每个元素的大小是(指针4字节+int[2]对象24字节=28字节),共128个,所以大小是128*28=3584字节,再加上第一纬的标记头8字节,class指针4字节,数组长度4字节,共3600字节,可看到存储相同量的元素,int[128][2]比int[256]多占用了(3600-1040=2560字节)的内存空间。

所以,在实际工作中,尽量避免使用多维数组,有需要也尽量使用一维数组来实现,将一维数组折叠一下就行了。

4:String

想要分析处String占用的内存大小,需要先来看下String的定义(只列出会占用堆内存的)

public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence {/** The value is used for character storage. */private final char value[];/** Cache the hash code for the string */private int hash; // Default to 0
}

结构如下:

在这里插入图片描述

则一个空string占用的大小是40字节,如果是不包括String对象头的话,则大小是24字节。

5:普通的类

如下的类:

public class EmptyObject {int a;byte b;Integer c = new Integer(10);
}

内存结构如下:

在这里插入图片描述

所以总大小是(标记字8字节+class指针4字节+int a 4字节+byte b 1字节+Integer c指针 4字节=21字节),再对齐,因此是24字节,如下验证:

public class Main {public static void main(String[] args) throws InterruptedException {List<EmptyObject> oneHundredList = new ArrayList<>();for (int i = 0; i < 100; i++) {oneHundredList.add(new EmptyObject());}Thread.sleep(Integer.MAX_VALUE);}
}
:\Users\Administrator>jps -l
13200 dongshi.daddy.objectsize.Main
12692
6824 sun.tools.jps.Jps
14892
19580 org.jetbrains.idea.maven.server.RemoteMavenServer36
3308 org.jetbrains.jps.cmdline.LauncherC:\Users\Administrator>jmap -histo 13200 | findstr "EmptyOb"28:           100           2400  dongshi.daddy.objectsize.EmptyObject

可以看到每个确实是24字节。

写在后面

参考文章列表

Java中的String到底占用多大的内存空间?带你一步步验证! 。


http://www.ppmy.cn/news/90549.html

相关文章

八、Spring Cloud Alibaba-seata分布式事务

一、引言 1、事务简介 事务(Transaction)是访问并可能更新数据中各种数据项的一个程序执行单元(unit&#xff09;。在关系数据库中&#xff0c;一个事务由一组SQL语向组成。事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。 原子性(atomic…

数据库提权

数据库提权的前提就是得到数据库的账号密码。在webshell或本地进行提权。 数据库提权分为四步&#xff1a; 1.服务探针&#xff0c;探测出数据库的类型&#xff08;端口扫描等&#xff09; 2.信息搜集&#xff0c;就是获取到数据库的账号密码。权限要高。 读取数据库密码的…

基于ROS2的costmap中Obstacle Layer中对障碍物信息的增加与删除机制的方案调研。

文章目录 1.背景2.目标3. 障碍物信息添加方式发送数据的数据结构与接收数据的数据结构 4. 障碍物清理机制4.1 参数过滤障碍物4.2 时间过滤障碍物4.3 优化光追算法过滤障碍物4.4 障碍物跟踪过滤障碍物4.4.1 无语义动态障碍物跟踪4.4.2 语义障碍物分层4.4.3 障碍物实例分割与类别…

使用CNN-LSTM来预测锂离子电池健康状态SOH(附代码)

对于电动汽车而言&#xff0c;动力锂电池的健康状态(State of Health,SOH)估算方法是电池管理系统中非常重要的一个方面。准确估计锂电池老化状态并预测电池剩余寿命对于电动汽车稳定安全运行有着重要的意义。借助数据驱动方法的思想&#xff0c;本文对锂离子电池寿命历史数据进…

C++ Primer第五版_第十八章习题答案(11~20)

文章目录 练习18.11练习18.12练习18.13练习18.14练习18.15练习18.16练习18.17练习18.18练习18.19练习18.20 练习18.11 为什么 what 函数不应该抛出异常&#xff1f; what中如果抛出异常&#xff0c;需要try catch捕获&#xff0c;再调用what&#xff0c;一直循环&#xff0c;直…

霍尔电流传感器的注意事项及其在直流列头柜中的应用

安科瑞虞佳豪 霍尔电流传感器​注意事项 &#xff08;1&#xff09;电流传感器必须根据被测电流的额定有效值适当选用不同的规格的产品。被测电流长时间超额&#xff0c;会损坏末极功放管&#xff08;指磁补偿式&#xff09;&#xff0c;一般情况下&#xff0c;2倍的过载电流…

“多杆合一”降本增效——数字孪生智慧灯杆

随着智慧城市建设的不断深入&#xff0c;智慧灯杆作为城市基础设施的重要组成部分&#xff0c;正在成为城市智能化和绿色化的重要手段之一。 图扑智慧灯杆系统在城市道路照明领域引入信息化手段&#xff0c;通过构建路灯物联网&#xff0c;实现了现代化的路灯按需维修和按需照…

【操作系统真象还原】第4章:保护模式入门(4.4~4.5节)

目录 4.4 处理器微架构简介 4.4.1 流水线 4.4.2 乱序执行 4.4.3 缓存 4.4.4 分支预测 4.5 使用远跳转指令清空流水线&#xff0c;更新段描述符缓冲寄存器 4.6 保护模式之内存段的保护 4.6.1 向段寄存器加载选择子时的保护 4.6.2 代码段和数据段的保护 4.6.3 栈段的保…