我在接受采访时被要求计算HashMap
的内存使用量,以及如果你有200万个项目,它将消耗多少估计内存。
例如:
Map <String,List<String>> mp=new HashMap <String,List<String>>();
映射是这样的。
key value
----- ---------------------------
abc ['hello','how']
abz ['hello','how','are','you']
我如何用Java估算这个HashMap对象的内存使用情况?
答案 0 :(得分:22)
简短回答
要了解对象的大小,我会使用分析器。例如,在YourKit中,您可以搜索对象,然后让它计算其深度。如果对象是独立的并且对象是保守的大小,这将让您充分了解将使用多少内存。
狡辩
如果物体的某些部分在其他结构中重复使用,例如字符串文字,你不会通过丢弃它来释放这么多内存。事实上,丢弃一个对HashMap的引用可能根本不会释放任何内存。
序列化怎么样?
对对象进行序列化是获得估计的一种方法,但由于序列化开销和编码在内存和字节流方面不同,因此它可能会大量关闭。使用了多少内存取决于JVM(以及它是否使用32/64位引用),但序列化格式始终相同。
e.g。
在Sun / Oracle的JVM中,整数可以占用16个字节用于标头,4个字节用于数字和4个字节填充(对象在内存中是8字节对齐),总共24个字节。但是,如果序列化一个整数,则需要81个字节,串行两个整数,它们需要91个字节。即第一个Integer的大小是膨胀的,第二个Integer小于内存中使用的大小。
字符串是一个更复杂的例子。在Sun / Oracle JVM中,它包含3个int
值和一个char[]
引用。所以你可以假设它使用16字节头加上3 * 4字节用于int
,4字节用于char[]
,16字节用于char[]
的开销,然后每个字节用两个字节char,对齐到8字节边界......
哪些标志可以改变大小?
如果您有64位引用,则char[]
引用的长度为8个字节,从而产生4个字节的填充。如果您有64位JVM,则可以使用+XX:+UseCompressedOops
来使用32位引用。 (所以单看JVM位大小并不能告诉你它的引用大小)
如果你有-XX:+UseCompressedStrings
,JVM将尽可能使用byte []而不是char数组。这可能会略微减慢您的应用程序速度,但可以显着提高您的内存消耗当使用byte []时,消耗的内存为每个字符1个字节。 ;)注意:对于4字符串,如示例所示,由于8字节边界,使用的大小相同。
“尺寸”是什么意思?
正如已经指出的那样,HashMap和List更复杂,因为很多(如果不是全部)可以重用Strings,可能是String文字。你所说的“大小”取决于它的使用方式。即该结构单独使用多少内存?如果结构被丢弃,将释放多少?如果复制结构,将使用多少内存?这些问题可以有不同的答案。
没有探查器,你能做什么?
如果您可以确定可能的保守尺寸,足够小,确切的尺寸无关紧要。保守的情况很可能是从头开始构造每个String和条目的地方。 (我只是说HashMap可以容纳10亿个条目,即使它是空的。带有单个字符串的字符串可以是一个包含20亿个字符的字符串的子字符串)
您可以执行System.gc(),获取空闲内存,创建对象,执行另一个System.gc()并查看可用内存减少了多少。您可能需要多次创建对象并取平均值。多次重复这个练习,但它可以给你一个合理的想法。
(顺便说一句,虽然System.gc()只是一个提示,但Sun / Oracle JVM默认每次都会执行一次完整的GC)
答案 1 :(得分:1)
我认为应该澄清这个问题,因为HashMap的大小与HashMap的大小+ HashMap包含的对象之间存在差异。
如果考虑HashMap的大小,在您提供的示例中,HashMap存储对String“aby”的一个引用和对List的一个引用。因此列表中的多个元素无关紧要。只有对列表的引用存储在值中。
在一个32位JVM中,在一个Map条目中,“aby”引用有4个字节,List引用有4个字节+ Map字节的“hashcode”int属性为4个字节+“4个字节”下一个“地图条目的属性。
您还添加了4 *(X-1)字节引用,其中“X”是HashMap在调用构造函数new HashMap<String,List<String>>()
时创建的空桶数
。根据{{3}},它应该是16。
还有loadFactor,modCount,threshold和size都是原始int类型(16个字节)和header(8bytes)。
所以最后,你上面的HashMap的大小是4 + 4 + 1 +(4 * 15)+ 16 + 8 = 93字节
这是基于HashMap拥有的数据的近似值。我想也许访谈者有兴趣看看你是否知道HashMap的工作方式(例如,默认构造函数创建和16个桶的数组用于Map条目,事实是存储在HashMap中的对象的大小不影响HashMap大小,因为它只存储引用)。
HashMap被如此广泛地使用,在某些情况下,应该值得使用具有初始容量和负载因子的构造函数。
答案 2 :(得分:0)
如果不知道所有字符串是什么,以及每个列表中有多少项,或者不知道字符串是否都是唯一引用,则事先无法知道。
唯一可以确定的方法是将整个事物序列化为字节数组(或临时文件),并查看确切的字节数。