Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >JVM系列之:String,数组和集合类的内存占用大小

JVM系列之:String,数组和集合类的内存占用大小

作者头像
程序那些事
发布于 2020-07-23 11:42:58
发布于 2020-07-23 11:42:58
66300
代码可运行
举报
文章被收录于专栏:程序那些事程序那些事
运行总次数:0
代码可运行

程序那些事

简介

之前的文章中,我们使用JOL工具简单的分析过String,数组和集合类的内存占用情况,这里再做一次更详细的分析和介绍,希望大家后面再遇到OOM问题的时候不再抱头痛哭,而是可以有章可循,开始吧。

数组

先看下JOL的代码和输出:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//byte arraylog.info("{}",ClassLayout.parseInstance("www.flydean.com".getBytes()).toPrintable());

输出结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
INFO com.flydean.CollectionSize - [B 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)                           22 13 07 00 (00100010 00010011 00000111 00000000) (463650)     12     4        (object header)                           0f 00 00 00 (00001111 00000000 00000000 00000000) (15)     16    15   byte [B.<elements>                             N/A     31     1        (loss due to the next object alignment)Instance size: 32 bytesSpace losses: 0 bytes internal + 1 bytes external = 1 bytes total

注意,本文的结论都在64位的JVM中运行得出了,并且开启了COOPs压缩对象指针技术。

可以看到数组对象的对象头大小是16字节,再加上数组里面的内容长度是15字节,再加上1位补全。最后得到的大小是32字节。

同样的,我们计算存有100个对象的数组,可以得到下面的结论:

注意最后面的Object数组,如果数组中存储的不是基础类型,那么实际上存储的是执行该对象的指针,该指针大小是4个字节。

String

String是一个非常特殊的对象,它的底层是以byte数组存储的。

注意,在JDK9之前,String的底层存储结构是char[],一个char需要占用两个字节的存储单位。

因为大部分的String都是以Latin-1字符编码来表示的,只需要一个字节存储就够了,两个字节完全是浪费。

于是在JDK9之后,字符串的底层存储变成了byte[]。

同样的我们还是用JOL来分析:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//Stringlog.info("{}",ClassLayout.parseInstance("www.flydean.com").toPrintable());

输出结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
INFO com.flydean.CollectionSize - java.lang.String object internals: OFFSET  SIZE      TYPE DESCRIPTION                               VALUE      0     4           (object header)                           05 00 00 00 (00000101 00000000 00000000 00000000) (5)      4     4           (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)      8     4           (object header)                           77 1a 06 00 (01110111 00011010 00000110 00000000) (399991)     12     4    byte[] String.value                              [119, 119, 119, 46, 102, 108, 121, 100, 101, 97, 110, 46, 99, 111, 109]     16     4       int String.hash                               0     20     1      byte String.coder                              0     21     1   boolean String.hashIsZero                         false     22     2           (loss due to the next object alignment)Instance size: 24 bytesSpace losses: 0 bytes internal + 2 bytes external = 2 bytes total

可以看到String中的对象头是12字节,然后加上4字节的指针指向一个byte数组。再加上hash,coder,和hasIsZero属性,最后的大小是24字节。

我这里使用的是JDK14的String版本,不同的版本可能有所不同。

当然这只是这个String对象的大小,不包含底层数组的大小。

我们来计算一下String对象的真实大小:

String对象的大小+byte数组的大小=24+32=56字节。

ArrayList

我们构建一个非常简单的ArrayList:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//Array Listlog.info("{}",ClassLayout.parseInstance(new ArrayList()).toPrintable());

输出结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
INFO com.flydean.CollectionSize - java.util.ArrayList object internals: OFFSET  SIZE                 TYPE DESCRIPTION                               VALUE      0     4                      (object header)                           05 00 00 00 (00000101 00000000 00000000 00000000) (5)      4     4                      (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)      8     4                      (object header)                           87 81 05 00 (10000111 10000001 00000101 00000000) (360839)     12     4                  int AbstractList.modCount                     0     16     4                  int ArrayList.size                            0     20     4   java.lang.Object[] ArrayList.elementData                     []Instance size: 24 bytesSpace losses: 0 bytes internal + 0 bytes external = 0 bytes total

画个图来直观的表示:

这里modCount和size的初始值都是0。

HashMap

因为文章篇幅的限制,这里就不把代码列出来了,我只贴个图上来:

HashSet

LinkedList

treeMap

来个比较复杂的TreeMap:

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-07-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序那些事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
JVM系列之:Contend注解和false-sharing
现代CPU为了提升性能都会有自己的缓存结构,而多核CPU为了同时正常工作,引入了MESI,作为CPU缓存之间同步的协议。MESI虽然很好,但是不当的时候用也可能导致性能的退化。
程序那些事
2020/07/24
6160
JVM系列之:Contend注解和false-sharing
对象实例化与内存布局(深入)
创建对象方式有:new、Class的newInstance()、Constructor的newInst(Xxx)、使用clone()、使用反序列化、第三方库Objenesis;
逍遥壮士
2021/05/24
1.2K0
对象实例化与内存布局(深入)
boolean 与boolean数组内存布局-Java快速进阶教程
首先,我们将检查 JVM 以查看对象大小。然后,我们将了解这些尺寸背后的基本原理。
jack.yang
2025/04/05
380
多线程基础(五):java对象的MarkWord及synchronized锁升级过程
在前面聊过了如何使用synchronized,以及synchronized不同的加锁方式分别锁的是哪些对象。本文对synchronized底层的原理进行深层次的分析。
冬天里的懒猫
2020/09/10
9460
多线程基础(五):java对象的MarkWord及synchronized锁升级过程
JVM系列之:String.intern和stringTable
StringTable是什么?它和String.intern有什么关系呢?在字符串对象的创建过程中,StringTable有起到了什么作用呢?
程序那些事
2020/07/28
4930
JVM系列之:String.intern和stringTable
终于我用JOL打破了你对java对象的所有想象
使用面向对象的编程语言的好处就是,虽然没有女朋友,但是仍然可以new对象出来。Java是面向对象的编程语言,我们天天都在使用java来new对象,但估计很少有人知道new出来的对象到底长的什么样子,是美是丑到底符不符合我们的要去?
程序那些事
2020/07/07
9980
多线程五 锁的膨胀过程
上一篇中,涉及到了锁升级的过程,也对其锁的升级有了一个大概的了解:单线程持有,在jvm延迟偏向的时间内是轻量级锁,之后为偏向锁,出现多个线程交替执行,对同一资源加锁会升级为轻量级锁,多个线程竞争拿不到锁会升级为重量级锁。在上一篇的基础上再进一步的了解锁升级的过程。
用针戳左手中指指头
2021/01/29
3010
线程并发带来的安全性问题 之 同步锁(二)
上一节我们学习了线程并发常见的安全性问题、锁的底层类型和对象结构的差异、锁升级相关知识。今天我们继续学习锁是如何升级的?
架构探险之道
2023/03/04
3410
线程并发带来的安全性问题 之 同步锁(二)
深入了解Java锁
继 打印Java对象头,我们再深入探索一下Java锁。无锁状态我们就不说了,下面我们一一打印偏向锁、轻量锁,重量锁的对象头。
@阿诚
2021/02/04
4520
垃圾收集分析(1)-Java对象结构(上)
GC(Garbage Collection)是目前很多编程语言自带的特性,例如Java,Python;GC是一个很好的特性,能让使用这个语言编程的程序员不去关心内存回收,并且降低内存泄漏和内存溢出发生的概率。 我们以Java语言JVM为例,从其对象结构和JVM运行时内存结构出发,针对其GC算法思路和实现进行分析,同时类比其他GC算法。 首先,在Java 8中,Java对象在内存中结构包括: 1. 类型指针:一个指向类信息的指针,描述了对象的类型。 2. 标记字(Mark Word):一组标记,描述了对象的状态,包括对象散列码(如果有)、对象的形状(是否是数组)、锁状态、数组长度(如果标记显示这个对象是数组,描述了数组的长度) 3. 对齐性填充:所有对象都是8字节对齐的 -> 也就是说,所有对象的起始位置都是满足A(A%8==0),所以对于有的对象需要这个对齐性填充来满足这个规则。 4. 域变量区域:这个对象的域变量所占用的内存。Java域变量存在两类:原始类型(primitive type)和普通对象指针(ordinary object pointer)。
干货满满张哈希
2021/04/12
3250
垃圾收集分析(1)-Java对象结构(上)
Java GC详解 - 1. 最全面的理解Java对象结构 - 对象指针 OOPs
GC(Garbage Collection) 是目前很多编程语言自带的特性,例如Java,Python;GC是一个很好的特性,能让使用这个语言编程的程序员不去关心内存回收,并且降低内存泄漏和内存溢出发生的概率。
干货满满张哈希
2021/04/12
9220
Java GC详解 - 1. 最全面的理解Java对象结构 - 对象指针 OOPs
每日一面 - JVM 类型字压缩指针与 JVM 最大内存有何关系?
压缩指针这个属性默认是打开的,可以通过-XX:-UseCompressedOops关闭。
干货满满张哈希
2021/04/12
3840
每日一面 - JVM 类型字压缩指针与 JVM 最大内存有何关系?
线程并发带来的安全性问题 之 同步锁(一)
在下面的案例中,演示了两个线程分别去去调用 demo.incr 方法来对 i 这个变量进行叠加,预期结果 应该是20000,但是实际结果却是小于等于20000的值。
架构探险之道
2023/03/04
2640
线程并发带来的安全性问题 之 同步锁(一)
[JAVA基础] - JVM对象内存布局及锁的标记位
一、对象布局 1、对象头 1)存储对象自身的运行时数据 hash码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。占位32/64位虚拟机分别占32/64个比特,官方称"Mark Word" 2)类型指针 指向对象的元数据,如果是数组,还会存储数组长度。 2、实例数据 3、对齐填充 要求对象是8的整数倍,对象头已经是8位的整数倍,只填充实例数据即可。 二、Object o = new Object()内存占用情况 占用16个字节 对象头12个字节,对齐填充4个字
夹胡碰
2022/05/19
4420
[JAVA基础] - JVM对象内存布局及锁的标记位
JVM - 剖析Java对象头Object Header之指针压缩
同一个对象, 不开启指针压缩 8字节 存入堆中和 开启指针压缩4字节存入堆中,哪个更好一些,显而易见。
小小工匠
2021/08/17
1K0
Java 对象在内存
1. Mark word: 记录线程,锁等对象状态,64位机占用8字节;32位机占用4字节; 当前主机是64位占8字节
一个架构师
2022/06/20
2480
Java 对象在内存
偏向锁批量重偏向与批量撤销
批量重偏向:当一个线程创建了大量对象并执行了初始的同步操作,后来另一个线程也来将这些对象作为锁对象进行操作,会导偏向锁重偏向的操作。 批量撤销:在多线程竞争剧烈的情况下,使用偏向锁将会降低效率,于是乎产生了批量撤销机制。 JVM的默认参数值: intx BiasedLockingBulkRebiasThreshold = 20 默认偏向锁批量重偏向阈值 intx BiasedLockingBulkRevokeThreshold = 40 默认偏向锁批量撤销阈值 批量重偏向 测试代码: pu
@阿诚
2021/02/04
1.5K0
初步了解Java对象布局
其中byte、short、int、long都是表示整数的,只不过他们的取值范围不一样
iiopsd
2023/10/17
1870
初步了解Java对象布局
并发基石-Markword与锁升级
synchronized关键字是java提供的互斥锁关键字,我们常说的互斥锁一般都是非自旋锁,即竞争不到锁的线程会进入阻塞状态知道被唤醒 今天我们来讲讲java中用来对synchronized进行优化的三种锁,同时会介绍markword对象头 目前我在网上搜到的十几篇博客讲的都有问题,可能有写对的我没搜到. 很多人不经过验证直接把markOop.hpp中的JavaThread*当成ThreadId这是错误的,实际是java线程在C语言的指针 并且未计算过hashCode和计算过hashCode的情况也是不一样的 本篇博客最后会展示使用jol工具,读取展示对象头的结果进行验证 附上openjdk的markOop.hpp链接
歪歪梯
2020/06/19
6262
【多线程与高并发】- 锁的机制与底层优化原理
最近经常研究一些关于线程并发的问题,再开发中也实实在在遇到过许多的并发问题,之前所学的是如何解决这些问题,然而接下来就得理解一下底层原理。
怒放吧德德
2024/09/06
2200
推荐阅读
相关推荐
JVM系列之:Contend注解和false-sharing
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验