本文主要是介绍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");}}}
实验结果
那接下来我们就来分析为啥会有这么多内存占用
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 类型的数据需要多少空间?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!