700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > Java对象内存大小计算

Java对象内存大小计算

时间:2021-04-19 06:37:31

相关推荐

Java对象内存大小计算

背景

今天办公室两个人事妹子因为一道Java试题各持己见,誓死捍卫自己的答案(对,是HR没错 —— 程序猿快没活路了)。

题:字符串 “7天学会JAVA” 占用的内存空间是 ( )

(A)8个字节 (B)11个字节 (C)15个字节 (D)16个字节

一方坚持是11(答11或14),理由是直接使用Java的 getBytes(“GBK或UTF-8”),获得结果。

另一方坚持16,理由是Java内存中均以Unicode存储,String由char数组存储,单个char的Unicode占用2个字节,所以8*2=16。

博主也未幸免于难,被拉入战斗,这种情况下输和赢都是输了,苦啊,但是那还是必须要有自己的立场的。我站在答案16这边,此时仇恨值急速上升……

这些东西呢,Java基础,答案也必然是唯一的,但是百度的答案也是两种都有,各展千秋。

本文给出分析依据和16的答案,不代表各第三方考试答案,大家可以评论区讨论交流。

解析

第一轮:结合相关资料我的第一轮总结的依据如下

1、Java语言使用的是Unicode字符集,每个字符在内存中占16位。

2、在unicode中,一个字符就是两个字节(所以大家经常听到char占2个字节,嗯)。

3、通常情况下:GBK GB2312 占据2个字节 (1个字符2个字节);UTF-8 占 3 个字节(1个字符3个字节但不完全绝对)。

4、Java的String使用的编码是Unicode、将“中”字转为Unicode编码,结果是 \u4e2d。

5、Unicode是一种“编码”,所谓编码就是一个编号(数字)到字符的一种映射关系,只是一种一对一的映射而已。

6、GBK、UTF-8是一种“编码格式”,是用来序列化或存储上一点中提到的那个“编号(数字)”的一种“格式”。

7、回到最开始,Java的String使用的编码是Unicode,当String存在于内存中时,是"只有编码而没有编码格式的",所以java程序中的任何String对象在内存中时,说它是GBK还是UTF-8都是错的,String在内存中不需要“编码格式”,它只是一个Unicode的串而已,你可以将内存中的英文单词以GBK输出到文件也可以以UTF-8输出到文件。

8、当字符串需要在网络中传输或要被写入文件时,就需要使用编码格式了,乱码问题也因此出现。

9、如上,编码格式一种显式的作用,而非本质内存存储作用,内存占用直接与Unicode数量有关。

10、String对象提供了codePointCount和codePointXXX等方法,就是为了以Unicode为单元进行操作的,所以它应该是以一个一个Unicode组成的。

第二轮:使用 jprofile 读取 JVM 内存查看

我读取了 “7天学会JAVA” 和 “7天学会JAVA1234”,两个字符串的信息。

如果按照答案 11 来计算,后面一个字符串的char数组部分应该是 15。如果按照 16 来计算,后面一个字符串char数组部分应该是 24。注:在11、15、16、24均是不不考虑头信息的情况下的内存占用,一个空的 char 数组占用16字节基础内存空间。

下面两张图给出了 jprofile 的结果,如下:

先埋个点,不然你看下图的时候可能会对 shallow size 的大小纳闷,就是HotSpot VM 的内存填充,大体来说就是内存占用是 8 的倍数(进位制) ,比如 24=24,25=32。

这两张图中,可以对 ShallowSize 进行对比,第一张图的上下两个char数组 false 和 lt 都可以作为对比参考(你也可以按照文初11和16的算法计算一下 false 和 lt 的结果)。

至此,文初的题目结果是11还是16,我想你应该有答案了。

对于 Shallow Size 有第三方库 Lucene 提供了API可以通过代码获得大小,如下:

maven 依赖

<dependency><groupId>org.apache.lucene</groupId><artifactId>lucene-core</artifactId><version>8.8.2</version></dependency>

代码示例

public static void main(String[] args) throws InterruptedException {// 空字符串System.out.println(RamUsageEstimator.sizeOf(""));String s = "7天学会JAVA";// 返回参数对象的综合整体大小(以字节为单位)long a = RamUsageEstimator.sizeOf(s);System.out.println(a);// 计算指定对象本身在堆空间的大小(一般不含内容),单位字节// 估计给定对象的“浅”内存使用量。对于数组,这将是由数组存储获取的内存(后面不跟任何子引用)。对于对象这将是字段JVM也会应用对象对齐。long b = RamUsageEstimator.shallowSizeOf(s);System.out.println(b);// 同sizeOf(),返回带有单位的可读的结果,如:56 bytes,可以指定单位// String c = RamUsageEstimator.humanReadableUnits(a);// System.out.println(c);}// 输出结果405624

下文还有一种使用 openjdk 的 jol 获取内存情况的

内存空间占用的组成和计算

组成

如上图,在 HotSpot VM 中,对象在内存中存储的布局由 对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)三部分组成。

对象头

HotSpot VM 的对象头包括两部分信息:

第一部分markword,用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,这部分数据的长度在32位和64位的虚拟机(未开启压缩指针)中分别为32bit和64bit,官方称它为“MarkWord”。对象头的另外一部分是klass,类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

实例数据

实例数据部分是对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容。无论是从父类继承下来的,还是在子类中定义的,都需要记录起来,即所有成员变量。

对齐填充

第三部分对齐填充并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。由于HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,就是对象的大小必须是8字节的整数倍。而对象头部分正好是8字节的倍数(1倍或者2倍),因此,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。

大小计算

在32位系统下,存放Class指针的空间大小是4字节,MarkWord是4字节,对象头为8字节。在64位系统下,存放Class指针的空间大小是8字节,MarkWord是8字节,对象头为16字节。64位开启指针压缩的情况下,存放Class指针的空间大小是4字节,MarkWord是8字节,对象头为12字节。数组长度4字节+数组对象头8字节(对象引用4字节(未开启指针压缩的64位为8字节)+数组markword为4字节(64位未开启指针压缩的为8字节))+对齐4=16字节。静态属性不算在对象大小内。

说明:其中ref表示引用类型,引用类型实际上是一个地址指针,32bit机器上,占用4字节,64bit机器上,在jdk1.6之后,如果开启了指针压缩(默认开启: -XX:UseCompressedOops,仅支持64位机器),则占用4字节。Java对象的所有字段类型都可映射为上述类型之一,因此实例数据部分的大小,实际上就是这些字段类型的大小之和。

关于大小的计算,也可以通过 openjdk 的 jol 来获得,如下:

依赖

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

代码示例

public static void main(String[] args) {// 几个常用方法:// 1) 查看对象内部信息: ClassLayout.parseInstance(obj).toPrintable()// 2) 查看对象外部信息:包括引用的对象:GraphLayout.parseInstance(obj).toPrintable()// 3) 查看对象占用空间总大小:GraphLayout.parseInstance(obj).totalSize()String s = "7天学会JAVA";System.out.print(ClassLayout.parseClass(String.class).toPrintable());System.out.print(GraphLayout.parseInstance(s).toPrintable());System.out.println(GraphLayout.parseInstance(s).totalSize());}

最后,有一篇文章写了具体的几个实例来计算内存占用的,需要的可以参考:

https://shanhy./article/details/115940003

(END)

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。