HashMap put 一百万个 kv 都是 Int 类型的数据需要多少空间?

2023-11-08 03:30

本文主要是介绍HashMap put 一百万个 kv 都是 Int 类型的数据需要多少空间?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

问题

代码实践

实验结果

Java对象内存布局

对象头:

实例数据:

对齐填充:

HashMap本身占用内存

Node占用内存

参考文章



本文内容来源于摘抄总结实践

 

问题

kv都是 int,一个 int 4 byte,就是100w * 8那就是8M,或者存储的是包装类 Integer ,一个 Integer 16 byte,那就是32 M 咯?

代码实践

通过java.lang.Runtime类中的freeMemory()方法来进行测试,输出freeMemory前,调用一次System.gc(),得出结论是百万键值对的HashMap占用大约69M空间。

package Java2;import java.util.HashMap;/*** @author ranbo* @version V1.0* @Title:* @Package Java2* @Description:* @date 2018/7/26 下午10:55*/
public class HashMapUseMemory {public static void main(String[] args) {double start = 0;double end = 0;System.gc();start = Runtime.getRuntime().freeMemory();  // 在java程序运行的过程的,内存总是慢慢的从操作系统那里挖的,基本上是用多少挖多少,但是java虚拟机100%的情况下是会稍微多挖一点的,// 这些挖过来而又没有用上的内存,实际上就是freeMemory()System.out.println("start: " + start);System.out.println("end: " + end);HashMap<Integer, Integer> hashMap = new HashMap<>();for (int i = 0; i < 1000000; i++) {hashMap.put(i, 128);   // value 为128 和 127 就是int和Integer的区别,(16 byte - 4 byte) * 100w就是 12 M的样子  IntegerCacheif(i % 10000 == 0) {end = Runtime.getRuntime().freeMemory();System.out.println("start: " + start);System.out.println("end: " + end);System.out.println("HashMap对象占内存:" + (end - start) + " byte");System.out.println("HashMap对象占内存:" + (end - start) / (1024 * 1024) + " M");}}end = Runtime.getRuntime().freeMemory();System.out.println("start: " + start);System.out.println("end: " + end);System.out.println("HashMap对象占内存:" + (end - start) + " byte");System.out.println("HashMap对象占内存:" + (end - start) / (1024 * 1024) + " M");System.out.println("----------清除了没有对象引用的对象");  // 即不可达对象System.gc(); // 清除了没有对象引用的对象,比如里面的临时变量,个人觉得 hash 也是end = Runtime.getRuntime().freeMemory();System.out.println("start: " + start);System.out.println("end: " + end);System.out.println("HashMap对象占内存:" + (end - start) + " byte");System.out.println("HashMap对象占内存:" + (end - start) / (1024 * 1024) + " M");}}}

实验结果

image.png

那接下来我们就来分析为啥会有这么多内存占用

Java对象内存布局

Java对象的内存布局包括:对象头(Header),实例数据(Instance Data)和补齐填充(Padding)。

对象头:

64位机器上,默认不开启指针压缩(-XX:-UseCompressedOops)的情况下,对象头占用12bytes,开启指针压缩(-XX:+UseCompressedOops)则占用16bytes。

实例数据:

原生类型(primitive type)的内存占用如下:

Primitive Type

Memory Required(bytes)

byte, boolean

1 byte

short, char

2 bytes

int, float

4 bytes

long, double

8 bytes

对象引用(reference)类型在64位机器上,关闭指针压缩时占用8bytes, 开启时占用4bytes。

对齐填充:

Java对象占用空间是8字节对齐的,即所有Java对象占用bytes数必须是8的倍数。例如,一个包含两个属性的对象:int和byte,并不是占用17bytes(12+4+1),而是占用24bytes(对17bytes进行8字节对齐)

HashMap本身占用内存

    /*** The table, initialized on first use, and resized as* necessary. When allocated, length is always a power of two.* (We also tolerate length zero in some operations to allow* bootstrapping mechanics that are currently not needed.)*/transient Node<K,V>[] table;/*** Holds cached entrySet(). Note that AbstractMap fields are used* for keySet() and values().*/transient Set<Map.Entry<K,V>> entrySet;/*** The number of key-value mappings contained in this map.*/transient int size;/*** The number of times this HashMap has been structurally modified* Structural modifications are those that change the number of mappings in* the HashMap or otherwise modify its internal structure (e.g.,* rehash).  This field is used to make iterators on Collection-views of* the HashMap fail-fast.  (See ConcurrentModificationException).*/transient int modCount;/*** The next size value at which to resize (capacity * load factor).** @serial*/// (The javadoc description is true upon serialization.// Additionally, if the table array has not been allocated, this// field holds the initial array capacity, or zero signifying// DEFAULT_INITIAL_CAPACITY.)int threshold;/*** The load factor for the hash table.** @serial*/final float loadFactor;

所以一个HashMap对象本身的大小为:

12(header) + 4(table reference) + 4(entrySet reference) + 4(size) + 4(modCount) + 4(threshold) + 8(loadFactor) + 4(keySet reference) + 4(values reference) = 48(bytes)

接着分析testMap实例在总共占用的内存大小。

Node占用内存

根据上面对HashMap原理的介绍,可知每对键值对对应一个Node对象。根据上面的Node的数据结构,一个Node对象的大小为:

     /*** Basic hash bin node, used for most entries.  (See below for* TreeNode subclass, and in LinkedHashMap for its Entry subclass.)*/static class Node<K,V> implements Map.Entry<K,V> {final int hash;final K key;V value;Node<K,V> next;Node(int hash, K key, V value, Node<K,V> next) {this.hash = hash;this.key = key;this.value = value;this.next = next;}}

 

12(header) + 4(hash reference) + 4(key reference) + 4(value reference) + 4(next pointer reference) = 28 (padding) -> 32(bytes)

加上Key和Value两个Integer对象,一个Node占用内存总大小为:32 + 2 * 16 = 64(bytes)

100w个就是 1000000 * 64

下面分析HashMap的Node数组的大小。

根据上面HashMap的原理可知,HashMap初为满足100w个node需要扩容到 1024 * 1024 * 2,这样乘以负载因子才会大于100w,所以Node[]占用的内存大小为:

Node[] = 16(header) + 1024 * 1024 * 2 * 4(Node reference) + 1000000 *64(Node)

所以,testMap占用的内存总大小为:

48(map itself) + (16 + (1024 * 1024 * 2 * 4) + 64 * 1000000)(Node[]) = = 72388672 byte

72388672 byte / (1024 *1024) = 69.03521728515625 M

 

 

 

 

 

 

 

 

参考文章

一个1000万HashMap,会占用多少空间内存?

Java对象内存占用分析

Java中HashMap结构自身占用的内存和Runtime类的freeMemory()等几个方法

 

这篇关于HashMap put 一百万个 kv 都是 Int 类型的数据需要多少空间?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/367653

相关文章

MyBatis-Plus通用中等、大量数据分批查询和处理方法

《MyBatis-Plus通用中等、大量数据分批查询和处理方法》文章介绍MyBatis-Plus分页查询处理,通过函数式接口与Lambda表达式实现通用逻辑,方法抽象但功能强大,建议扩展分批处理及流式... 目录函数式接口获取分页数据接口数据处理接口通用逻辑工具类使用方法简单查询自定义查询方法总结函数式接口

SQL中如何添加数据(常见方法及示例)

《SQL中如何添加数据(常见方法及示例)》SQL全称为StructuredQueryLanguage,是一种用于管理关系数据库的标准编程语言,下面给大家介绍SQL中如何添加数据,感兴趣的朋友一起看看吧... 目录在mysql中,有多种方法可以添加数据。以下是一些常见的方法及其示例。1. 使用INSERT I

Ubuntu如何分配​​未使用的空间

《Ubuntu如何分配​​未使用的空间》Ubuntu磁盘空间不足,实际未分配空间8.2G因LVM卷组名称格式差异(双破折号误写)导致无法扩展,确认正确卷组名后,使用lvextend和resize2fs... 目录1:原因2:操作3:报错5:解决问题:确认卷组名称​6:再次操作7:验证扩展是否成功8:问题已解

Python使用vllm处理多模态数据的预处理技巧

《Python使用vllm处理多模态数据的预处理技巧》本文深入探讨了在Python环境下使用vLLM处理多模态数据的预处理技巧,我们将从基础概念出发,详细讲解文本、图像、音频等多模态数据的预处理方法,... 目录1. 背景介绍1.1 目的和范围1.2 预期读者1.3 文档结构概述1.4 术语表1.4.1 核

PostgreSQL的扩展dict_int应用案例解析

《PostgreSQL的扩展dict_int应用案例解析》dict_int扩展为PostgreSQL提供了专业的整数文本处理能力,特别适合需要精确处理数字内容的搜索场景,本文给大家介绍PostgreS... 目录PostgreSQL的扩展dict_int一、扩展概述二、核心功能三、安装与启用四、字典配置方法

MySQL 删除数据详解(最新整理)

《MySQL删除数据详解(最新整理)》:本文主要介绍MySQL删除数据的相关知识,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录一、前言二、mysql 中的三种删除方式1.DELETE语句✅ 基本语法: 示例:2.TRUNCATE语句✅ 基本语

MyBatisPlus如何优化千万级数据的CRUD

《MyBatisPlus如何优化千万级数据的CRUD》最近负责的一个项目,数据库表量级破千万,每次执行CRUD都像走钢丝,稍有不慎就引起数据库报警,本文就结合这个项目的实战经验,聊聊MyBatisPl... 目录背景一、MyBATis Plus 简介二、千万级数据的挑战三、优化 CRUD 的关键策略1. 查

python实现对数据公钥加密与私钥解密

《python实现对数据公钥加密与私钥解密》这篇文章主要为大家详细介绍了如何使用python实现对数据公钥加密与私钥解密,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录公钥私钥的生成使用公钥加密使用私钥解密公钥私钥的生成这一部分,使用python生成公钥与私钥,然后保存在两个文

mysql中的数据目录用法及说明

《mysql中的数据目录用法及说明》:本文主要介绍mysql中的数据目录用法及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、版本3、数据目录4、总结1、背景安装mysql之后,在安装目录下会有一个data目录,我们创建的数据库、创建的表、插入的

MySQL之InnoDB存储页的独立表空间解读

《MySQL之InnoDB存储页的独立表空间解读》:本文主要介绍MySQL之InnoDB存储页的独立表空间,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、独立表空间【1】表空间大小【2】区【3】组【4】段【5】区的类型【6】XDES Entry区结构【