背景

今天办公室两个人事妹子因为一道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);}// 输出结果
40
56
24

下文还有一种使用 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.blog.csdn.net/article/details/115940003


(END)

Java对象内存大小计算相关推荐

  1. java统计空间占用_JVM —— Java 对象占用空间大小计算

    引用类型(reference type: Integer)在 32 位系统上每一个占用 4bytes(即32bit, 才干管理 2^32=4G 的内存), 在 64 位系统上每一个占用 8bytes( ...

  2. JVM —— Java 对象占用空间大小计算

    零. 为什么要知道 Java 对象占用空间大小 缓存的实现: 在设计 JVM 内缓存时(不是借助 Memcached. Redis 等), 需要知道缓存的对象是否会超过 JVM 最大堆限制, 如果会超 ...

  3. Java对象内存空间大小计算

    一.查看基础类型的对象内存大小 八股文中很明确的告诉你了基础类型的大小 ,如下图: 类型 值大小(byte) 对象内存大小(byte) 备注 byte 1 16 char 2 16 int 4 16 ...

  4. JVM概念之Java对象的大小与引用类型

    2019独角兽企业重金招聘Python工程师标准>>> 本文来自和你在一起的博客,原文标题:<JVM调优总结(二)-一些概念>.本文总结了JVM概念中的Java对象的大小 ...

  5. Java对象内存结构

    转载自 Java对象内存结构 学C/C++出身的我,对Java有一点非常困惑,那就是缺乏计算对象占用内存大小的机制.而在C++中就可以通过sizeof运算符来获得基本类型以及类实例的大小.C和C++中 ...

  6. JOL(java object layout): java 对象内存布局

    我们天天都在使用java来new对象,但估计很少有人知道new出来的对象到底长的什么样子?对于普通的java程序员来说,可能从来没有考虑过java中对象的问题,不懂这些也可以写好代码.今天,给大家介绍 ...

  7. linux看java堆大小,linux 改java堆内存大小

    linux 改java堆内存大小 [2021-02-08 23:06:29]  简介: linux查内存大小的方法:首先打开应用程序:然后选择系统工具选项,并单击系统终端选项:接着在系统终端命令行输入 ...

  8. 如何计算Java对象的大小

    一个对象通常由头和内容组成.想要计算一个对象的大小,我们就需要分别计算头部的大小和内容的大小. 查看一个对象的大小 首先在pom文件中引入apache下面的lucene-core依赖,然后调用对应的s ...

  9. http://www.dewen.net.cn/q/6120/如何获取Java对象的大小

    http://www.dewen.net.cn/q/6120/如何获取Java对象的大小 kenvi 1 票 kenvi 2078 在C或者C++里经常会通过sizeof来计算一个对象所占空间的大小, ...

最新文章

  1. 函数05 - 零基础入门学习C语言36
  2. NS_OPTIONS枚举的用法
  3. 《python数据分析实战》第七章手写
  4. c#写图像tif gdal_C# GDAL显示TIF
  5. 搜索引擎利用机器学习排序
  6. linux 免密码登录
  7. Java代码如何运行在Java虚拟机中
  8. OpenGL超级宝典(第7版)环境配置和相关问题
  9. java数组里的索引越界问题、空指针异常问题
  10. 【BP回归预测】基于matlab鲸鱼算法优化BP神经网络回归预测(多输入单输出)【含Matlab源码 1554期】
  11. java ini_Java读取ini文件 [org.dtools.javaini] | 学步园
  12. unshift()向数组的开头添加一个或更多元素,并返回新的长度
  13. win10便签常驻桌面_便签,草图,截屏草图,一个win10自带的小工具统统解决!...
  14. 使用java,计算一段文本中出现英语单词次数最多的单词
  15. MySQL查看当前数据库
  16. 1小时1篇文学会用python进行AI修复!
  17. pythoc_autocad_标注_all_横线_竖线
  18. PI数据库开发-java(读写pi中的时序数据和关系数据)
  19. NGSIM数据集提取换道前4s周围车辆的特征数据
  20. uniapp 微信小程序 上传图片到服务器

热门文章

  1. 用python制作电子时钟包装_使用电子水墨屏和树莓派制作的时钟,Epaper_clock
  2. java利用poi导出word文档
  3. webdriver设置浏览器全屏及设置浏览器窗口为特定大小的方法
  4. h5 上 删除 交互_iH5高级教程:H5交互进阶,擦一擦效果
  5. sync.Pool 使用
  6. csv逗号分割不兼容 解决_excel保存为csv 不兼容的功能
  7. 闪电鸡步数银行:自动挂机赚,0.3起提秒到!
  8. FortiClient VPN连接至98%时报错:Unable to establish the VPN connection.(E=98,T-981011001,M99,R10)
  9. C语言画圆(详细解释易懂)
  10. 科目三-上海松江小昆山