在Java中,空HashMap空间分配

时间:2011-02-28 21:41:09

标签: java hashmap memory-management

如何在添加任何元素之前判断预先调整大小的HashMap占用多少空间?例如,我如何确定以下内存占用多少内存?

HashMap<String, Object> map = new HashMap<String, Object>(1000000);

8 个答案:

答案 0 :(得分:4)

原则上,你可以:

  • 按理论计算:
    • 查看HashMap的实现,找出这个方法的作用。
    • 查看VM的实现,了解各个创建对象占用的空间。
  • 以某种方式测量它。

其他大多数答案都是关于第二种方式的,所以我会看第一种方法(在OpenJDK源代码中,1.6.0_20)。

构造函数使用capacity,它是两个&gt; =你的initialCapacity参数的下一个幂,因此在我们的例子中1048576 = 2 ^ 20。 然后,它会创建new Entry[capacity]并将其分配给table变量。 (另外它分配了一些原始变量)。

所以,我们现在有一个非常小的HashMap对象(它只包含3个int,一个float和一个引用变量),还有一个非常大的Entry []对象。这个数组需要空间用于它们的数组元素(它们是普通的引用变量)和一些元数据(大小,类)。

因此,它归结为参考变量有多大。这取决于VM实现 - 通常在32位VM中为32位(= 4字节),在64位VM中为64位(= 8字节)。

因此,基本上在32位虚拟机上,您的阵列占用4 MB,在64位虚拟机上占用8 MB,加上一些微小的管理数据。


如果随后使用映射填充HashTable,则每个映射都对应一个Entry对象。此条目对象由一个int和三个引用组成,在32位VM上占用大约24个字节,在64位VM上占两倍。因此,您的1000000映射HashMap(假设加载因子> 1)在32位VM上需要大约28 MB,在64位VM上大约需要56 MB。

当然还有键和值对象本身。

答案 1 :(得分:2)

您可以在创建变量之前和之后检查内存使用情况。例如:

long preMemUsage = Runtime.getRuntime().totalMemory() -
      Runtime.getRuntime().freeMemory();
HashMap<String> map = new HashMap<String>(1000000);
long postMemUsage = Runtime.getRuntime().totalMemory() -
      Runtime.getRuntime().freeMemory();

答案 2 :(得分:2)

具体答案取决于您使用的Java版本,JVM供应商和目标平台,最好通过直接测量确定,如其他答案中所述。

但作为一个简单的估计,对于32位或64位jvm,大小可能分别是~4 * 2^20~8 * 2^20个字节。

推理:

  • HashMap的Sun Java 1.6实现有一个固定的顶级对象和一个table字段,指向对哈希链的引用数组。

  • 在新创建的(空)HashMap中,引用都是null,并且数组大小是提供的initialCapacity的两倍大的下一个幂。 (是的......我检查了源代码。)

  • 引用在典型的32位JVM上占用4个字节,在典型的64位JVM上占用8个字节。一些64位JVM支持紧凑引用(“压缩oops”),但是你需要设置JVM选项来启用它。

  • 顶级对象有5个字段,包括table数组引用,但这是一个相对较小的常量开销。

  • 顶层对象和数组有对象头开销,但这些是常量且相对较小。

因此table数组的大小占主导地位,并且2^20(下一个2的幂大于1,000,000)乘以引用的大小。


因此,这告诉您设置较大的初始容量确实会占用大量内存。另一方面,如果初始容量是完全填充后地图容量的良好估计,则可以通过设置来节省大量时间。 (这避免了重新分配数组和重建哈希链的许多循环。)

答案 3 :(得分:1)

您可以使用VisualVM之类的探查器并跟踪内存使用情况。

也看看这个:http://www.velocityreviews.com/forums/t148009-java-hashmap-size.html

答案 4 :(得分:1)

我看一下这篇文章:http://www.javaworld.com/javaworld/javatips/jw-javatip130.html

简而言之,java没有C风格的sizeof运算符。您可以使用分析工具,但IMO上面的链接提供了最简单的解决方案。

另一条可能有用的信息:空java字符串占用40个字节。其中一百万可能至少有40MB ......

答案 5 :(得分:0)

您应该能够使用VisualVM(随JDK 6一起提供,或者可以是downloaded)来创建内存快照并检查分配的对象的大小。

HTH

答案 6 :(得分:0)

我同意分析器确实是唯一能说出来的方法。另一部分相关信息是您使用的是32位还是64位JVM。内存引用(指针)引起的开销量因此而异,以及是否打开了压缩oops。我发现对于较小的数据集,对象和指针的开销很大。

答案 7 :(得分:0)

在最新版本的Java 1.7(我正在看1.7.0_55)中,HashMap实际上懒惰地实例化其内部表。它只在调用put()时实例化 - 请参阅私有方法“inflateTable()”。所以你的HashMap,在你添加任何东西之前,至少会占用对象开销和实例字段的少数几个字节。