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的样子  IntegerCache
            if(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()等几个方法

 

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值