如何在添加任何元素之前判断预先调整大小的HashMap占用多少空间?例如,我如何确定以下内存占用多少内存?
HashMap<String, Object> map = new HashMap<String, Object>(1000000);
答案 0 :(得分:4)
原则上,你可以:
其他大多数答案都是关于第二种方式的,所以我会看第一种方法(在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,在你添加任何东西之前,至少会占用对象开销和实例字段的少数几个字节。