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

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

目录



简介



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


数组



先看下JOL的代码和输出:


//byte array
log.info("{}",ClassLayout.parseInstance("www.flydean.com".getBytes()).toPrintable());


输出结果:


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 bytes
Space losses: 0 bytes internal + 1 bytes external = 1 bytes total


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


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


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


image.png


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


String



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


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


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


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


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


//String
log.info("{}",ClassLayout.parseInstance("www.flydean.com").toPrintable());


输出结果:


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 bytes
Space losses: 0 bytes internal + 2 bytes external = 2 bytes total


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


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


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


image.png


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


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


ArrayList



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


//Array List
log.info("{}",ClassLayout.parseInstance(new ArrayList()).toPrintable());


输出结果:


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 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total


画个图来直观的表示:


image.png


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


HashMap



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


image.png


HashSet


image.png

LinkedList


image.png

treeMap


来个比较复杂的TreeMap:


image.png


总结



本文用图形的形式形象的展示了集合对象,数组和String在内存中的使用情况。


后面的几个集合我就没有一一计算,有兴趣的朋友可以在下方回复你计算的结果哟。

相关文章
|
20天前
|
API 索引
String类下常用API
String类下常用API
32 1
|
20天前
for循环和String类下方法的一个练习题
for循环和String类下方法的一个练习题
42 1
|
22天前
|
Java API 索引
【Java基础面试二十四】、String类有哪些方法?
这篇文章列举了Java中String类的常用方法,如`charAt()`、`substring()`、`split()`、`trim()`、`indexOf()`、`lastIndexOf()`、`startsWith()`、`endsWith()`、`toUpperCase()`、`toLowerCase()`、`replaceFirst()`和`replaceAll()`,并建议面试时展示对这些方法的熟悉度,同时深入理解部分方法的源码实现。
【Java基础面试二十四】、String类有哪些方法?
|
12天前
|
存储 程序员 Python
Python类的定义_类和对象的关系_对象的内存模型
通过类的定义来创建对象,我们可以应用面向对象编程(OOP)的原则,例如封装、继承和多态,这些原则帮助程序员构建可复用的代码和模块化的系统。Python语言支持这样的OOP特性,使其成为强大而灵活的编程语言,适用于各种软件开发项目。
14 1
|
21天前
|
存储 SQL Java
Java 系类之 Java StringBuffer类详解
这篇文章详细介绍了Java中的StringBuffer类的使用,包括其构造方法、追加字符串、替换字符、反转字符串和删除字符串的方法,并提供了相应的示例代码。
|
21天前
|
安全 Java API
Java系类 之 String、StringBuffer和StringBuilder类的区别
这篇文章讨论了Java中`String`、`StringBuffer`和`StringBuilder`三个类的区别,其中`String`是不可变的,而`StringBuffer`是线程安全的可变字符串类,`StringBuilder`是非线程安全的可变字符串类,通常在单线程环境下性能更优。
Java系类 之 String、StringBuffer和StringBuilder类的区别
|
27天前
|
编译器 C++
virtual类的使用方法问题之C++类中的非静态数据成员是进行内存对齐的如何解决
virtual类的使用方法问题之C++类中的非静态数据成员是进行内存对齐的如何解决
|
1月前
|
存储 程序员 API
八股day05_API、String对象和集合
day05_API、String对象和集合
|
27天前
|
缓存 程序员
封装一个给 .NET Framework 用的内存缓存帮助类
封装一个给 .NET Framework 用的内存缓存帮助类
|
8天前
|
存储 C++
C++(五)String 字符串类
本文档详细介绍了C++中的`string`类,包括定义、初始化、字符串比较及数值与字符串之间的转换方法。`string`类简化了字符串处理,提供了丰富的功能如字符串查找、比较、拼接和替换等。文档通过示例代码展示了如何使用这些功能,并介绍了如何将数值转换为字符串以及反之亦然的方法。此外,还展示了如何使用`string`数组存储和遍历多个字符串。

热门文章

最新文章