map<Long,Long> resultCache = new HashMap<Long,Long>(1000000),100w数据存满时,粗估resultCache大约占多少内存?

一、map<Long,Long> resultCache = new HashMap<Long,Long>(1000000),100w数据存满时,粗估resultCache大约占多少内存?

Java中HashMap的内存占用取决于多个因素,包括键值对的数量、负载因子、容量以及每个对象的内存占用。对于HashMap<Long, Long>,我们可以大致估算其内存占用。

假设我们有一个HashMap<Long, Long>,其初始容量设置为100万(1,000,000),默认负载因子为0.75。

  1. 每个Long对象占用内存:在64位JVM上,一个Long对象通常占用24字节(16字节对象开销 + 8字节数据)。
  2. HashMap节点占用内存:每个HashMap节点(HashMap.Node)大约占用16字节(对象头部分)+ 8(hash)+ 8(key引用)+ 8(value引用)+ 8(next引用)= 48字节。
  3. 数组占用HashMap内部使用数组存储节点,数组的每个槽位占用8字节(64位系统下的引用大小)。

假设HashMap的容量扩展到足以容纳100万个键值对,实际容量会是大于或等于100万的最小2的幂次,即1048576(2^20)。因此,数组部分大约占用8 * 1048576 = 8388608字节(8MB)。

接下来,计算节点和对象的总占用:

  • 每个键值对占用:24(key的Long对象)+ 24(value的Long对象)+ 48(节点)= 96字节。
  • 100万个键值对占用:96 * 1000000 = 96000000字节(约 91.6 MB)。

因此,总的大致估算内存占用为数组部分的 8MB 加上键值对部分的 91.6 MB,总共约 99.6 MB。 这还不包括Java虚拟机可能为了优化分配和管理内存而额外使用的内存。

请注意,这只是一个粗略的估算,实际内存占用可能会因JVM的实现、垃圾收集器的行为、内存对齐以及其他因素而有所不同。

二、在64位JVM上,一个Long对象通常占用24字节(16字节对象开销 + 8字节数据)。

在64位JVM上,一个Long对象的内存占用确实通常为24字节。这个计算来源如下:

  1. 对象头(Object Header): 12字节

    • 标记字(Mark Word): 8字节
    • 类型指针(Klass Pointer): 4字节
  2. 实例数据(Instance Data): 8字节

    • long值: 8字节
  3. 对齐填充(Padding): 4字节

    • 为了满足8字节对齐要求

计算过程:

  • 12(对象头)+ 8(实例数据)= 20字节
  • 20字节不是8的倍数,所以需要填充4字节
  • 最终:20 + 4 = 24字节

这种内存布局是HotSpot JVM在默认情况下的典型实现。但需要注意:

  1. 实际内存占用可能因JVM实现和配置而异。
  2. 使用压缩指针(-XX:+UseCompressedOops,默认开启)可能会改变这个计算。
  3. 某些JVM优化可能会影响对象的内存布局。

因此,24字节是一个常见的估算值,但在特定环境下可能会有所不同。

三、HashMap节点占用内存:每个HashMap节点(HashMap.Node)大约占用16字节(对象头部分)+ 8(hash)+ 8(key引用)+ 8(value引用)+ 8(next引用)= 48字节。

在64位JVM上,即使启用了压缩指针(Compressed Oops),对象头通常是由两部分组成:一个8字节的标记字(Mark Word)和一个4字节的类型指针。但是,由于对象的大小需要按照8字节对齐,所以实际上对象头在很多情况下会被视为占用16字节,以满足对齐要求。

对于HashMap.Node对象的内存布局,我们应该这样考虑:

  1. 对象头: 通常是16字节(包括对齐)

    • 标记字(Mark Word): 8字节
    • 类型指针(Klass Pointer): 4字节
    • 对齐填充(如果需要): 4字节,以确保对象头加上类型指针总共占用16字节,满足8字节对齐的要求。
  2. 实例数据:

    • int hash: 4字节 + 4字节填充
    • K key: 8字节(引用)
    • V value: 8字节(引用)
    • Node<K,V> next: 8字节(引用)

计算过程:

  • 对象头: 16字节
  • 实例数据: 4+4(int hash)+ 8(K key引用)+ 8(V value引用)+ 8(Node<K,V> next引用)= 32字节
  • 总计:16(对象头)+ 32(实例数据)= 48字节

四、数组占用HashMap内部使用数组存储节点,数组的每个槽位占用8字节(64位系统下的引用大小)。

在64位操作系统上:

  1. 未开启压缩指针:在64位JVM上,如果未开启压缩指针,一个对象引用通常占用8字节。这是因为64位系统可以使用的地址空间远大于32位,所以需要更多的位来表示内存地址。

  2. 开启压缩指针:为了减少内存使用,提高性能,JVM引入了压缩指针的概念。当开启压缩指针时,即使在64位系统上,对象引用可以被压缩到4字节。这是通过限制堆的最大大小(例如,小于32GB),使得不需要完整的64位来寻址每个对象,从而实现的。

    这是通过限制堆的最大大小(例如,小于32GB),使得不需要完整的64位来寻址每个对象,从而实现的。解释:
    这个概念涉及到JVM中压缩指针(Compressed Oops)的工作原理:

    1. 地址空间
      在64位系统中,理论上可以使用64位来表示内存地址,这意味着可以寻址高达2^64字节(约16EB)的内存空间。

    2. 实际需求
      但实际上,大多数应用程序不需要如此大的内存空间。通常,几十GB的堆内存就足够了。

    3. 压缩指针的原理
      JVM通过限制堆的最大大小(例如32GB),可以用更少的位来表示对象的地址。

    4. 如何实现

      • 假设堆的大小限制在32GB以内。
      • 32GB = 2^35 字节
      • 这意味着只需要35位就可以表示堆中的任何地址。
    5. 对齐和优化

      • JVM进一步将对象按8字节对齐。
      • 这意味着对象的地址总是8的倍数。
      • 因此,地址的最后3位总是000。
    6. 结果

      • 实际上只需要32位(35 - 3 = 32)就可以表示堆中的任何对象地址。
      • 32位正好可以存储在一个4字节的整数中。
    7. 好处

      • 引用(指针)从8字节减少到4字节。
      • 这不仅节省了内存,还提高了缓存效率。

    通过这种方式,JVM能够在64位系统上使用32位(4字节)的引用,而不是完整的64位(8字节),从而大大减少了内存使用,同时保持了对较大堆(最多 32GB)的支持。

因此,当我提到数组的每个槽位占用8字节时,是基于未开启压缩指针的假设。如果开启了压缩指针,并且堆的大小允许,那么每个槽位的引用大小可以减少到4字节。

在实际应用中,是否开启压缩指针以及选择的堆大小,将直接影响到对象引用的大小,从而影响到整个HashMap的内存占用。对于大多数现代JVM实现,默认情况下是开启压缩指针的,但这也取决于具体的JVM配置和版本。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值