Java中,空HashMap空间分配
Posted
技术标签:
【中文标题】Java中,空HashMap空间分配【英文标题】:In Java, empty HashMap space allocation 【发布时间】:2011-07-06 02:17:12 【问题描述】:在添加任何元素之前,我如何知道预先确定大小的 HashMap 占用了多少空间?例如我如何确定以下占用多少内存?
HashMap<String, Object> map = new HashMap<String, Object>(1000000);
【问题讨论】:
为什么,创建地图时不需要您可能可以使用像 VisualVM 这样的分析器并跟踪内存使用情况。
也看看这个:http://www.velocityreviews.com/forums/t148009-java-hashmap-size.html
【讨论】:
【参考方案2】:我想看看这篇文章:http://www.javaworld.com/javaworld/javatips/jw-javatip130.html
简而言之,java 没有 C 风格的 sizeof
运算符。您可以使用分析工具,但 IMO 上面的链接提供了最简单的解决方案。
另一条可能有用的信息:一个空的 java 字符串占用 40 个字节。其中一百万可能至少有 40MB...
【讨论】:
另外,我相信哈希映射分配的内部表大于请求的大小。我相信它通过找到比您请求的大小大 2 的最小幂来做到这一点。【参考方案3】:您可以在创建变量之前和之后检查内存使用情况。例如:
long preMemUsage = Runtime.getRuntime().totalMemory() -
Runtime.getRuntime().freeMemory();
HashMap<String> map = new HashMap<String>(1000000);
long postMemUsage = Runtime.getRuntime().totalMemory() -
Runtime.getRuntime().freeMemory();
【讨论】:
为了使其更可靠: - 在测量内存之前运行 System.gc() 10 次 - 构建 1000 个表并取平均值 我发布的链接解释了为什么这种方法不准确【参考方案4】:您应该能够使用 VisualVM(JDK 6 附带或可以是 downloaded)创建内存快照并检查分配的对象的大小。
【讨论】:
【参考方案5】:我同意分析器确实是唯一的判断方法。其他相关信息是您使用的是 32 位还是 64 位 JVM。由于内存引用(指针)而产生的开销取决于您是否打开了压缩 oops。我发现对于较小的数据集,对象和指针的开销很大。
【讨论】:
【参考方案6】:确切答案取决于您使用的 Java 版本、JVM 供应商和目标平台,最好通过直接测量来确定,如其他答案中所述。
但作为一个简单的估计,大小可能是 ~4 * 2^20
或 ~8 * 2^20
字节,分别用于 32 位或 64 位 jvm。
推理:
HashMap 的 Sun Java 1.6 实现有一个固定侧的***对象和一个指向哈希链引用数组的 table
字段。
在一个新创建的(空)HashMap 中,引用都是null
,并且数组大小是提供的initialCapacity
的第二次幂。 (是的......我检查了源代码。)
一个引用在典型的 32 位 JVM 上占用 4 个字节,在典型的 64 位 JVM 上占用 8 个字节。一些 64 位 JVM 支持紧凑引用(“compressed oops”),但您需要设置 JVM 选项以启用此功能。
顶部对象有 5 个字段,包括 table
数组引用,但这是一个相对较小的常量开销。
顶部对象和数组有对象头开销,但这些开销是恒定的且相对较小。
因此,table
数组的大小占主导地位,它是 2^20
(大于 1,000,000
的 2 的下一个幂)乘以引用的大小。
因此,这告诉您设置较大的初始容量确实会占用大量内存。另一方面,如果初始容量是完全填充时地图容量的良好估计值,则通过设置它可以节省大量时间。 (这避免了重新分配数组和重建哈希链的多次循环。)
【讨论】:
【参考方案7】:原则上,您可以:
理论计算: 查看 HashMap 的实现,了解此方法的作用。 查看 VM 的实现以了解各个创建的对象占用了多少空间。 以某种方式测量它。其他大部分答案都是关于第二种方式的,所以我会看第一种(在 OpenJDK 源代码中,1.6.0_20)。
构造函数使用capacity
,它是二的下一个幂 >= 您的 initialCapacity 参数,因此在我们的例子中是 1048576 = 2^20。
然后它创建一个new Entry[capacity]
并将其分配给table
变量。 (另外它分配了一些原始变量)。
所以,我们现在有一个非常小的 HashMap 对象(它只包含 3 个整数、一个浮点数和一个引用变量)和一个非常大的 Entry[] 对象。这个数组需要空间来存放它们的数组元素(它们是普通的引用变量)和一些元数据(大小、类)。
所以,这取决于参考变量有多大。这取决于 VM 实现 - 通常在 32 位 VM 中是 32 位(= 4 字节),在 64 位 VM 中是 64 位(= 8 字节)。
因此,基本上在 32 位 VM 上,您的阵列需要 4 MB,在 64 位 VM 上需要 8 MB,再加上一些微小的管理数据。
如果您随后使用映射填充 HashTable,则每个映射对应于一个 Entry
对象。此条目对象由一个 int 和三个引用组成,在 32 位 VM 上占用大约 24 个字节,在 64 位 VM 上可能是两倍。因此,您的 1000000 映射 HashMap(假设负载因子 > 1)在 32 位 VM 上将占用 ~28 MB,在 64 位 VM 上占用 ~56 MB。
当然,除了键和值对象本身。
【讨论】:
【参考方案8】:在最新版本的 Java 1.7(我正在查看 1.7.0_55)中,HashMap 实际上延迟实例化其内部表。它仅在调用 put() 时实例化 - 请参阅私有方法“inflateTable()”。因此,至少在向其添加任何内容之前,您的 HashMap 将只占用少数几个字节的对象开销和实例字段。
【讨论】:
以上是关于Java中,空HashMap空间分配的主要内容,如果未能解决你的问题,请参考以下文章