如何计算Java中的HashMap内存使用情况?

我在接受采访时被要求计算HashMap的内存使用量,以及如果你有200万个项目它会消耗多少估计内存。

例如:

 Map <String,List> mp=new HashMap <String,List>(); 

映射是这样的。 一个键作为字符串,一个字符串数组作为键。

 key value ----- --------------------------- abc ['hello','how'] abz ['hello','how','are','you'] 

我如何估计Java中这个HashMap对象的内存使用情况?

简短的回答

为了找出对象有多大,我会使用一个分析器。 例如,在YourKit中,您可以搜索该对象,然后让它计算其深度。 如果对象是独立的并且对象是保守的大小,这将让您充分了解将使用多少内存。

狡辩

如果对象的某些部分在其他结构中重复使用,例如字符串文字,则不会通过丢弃它来释放这么多内存。 事实上,丢弃对HashMap的一个引用可能根本不会释放任何内存。

序列化怎么样?

序列化对象是获得估计的一种方法,但由于序列化开销和编码在内存和字节流方面不同,因此它可能会大量关闭。 使用了多少内存取决于JVM(以及它是否使用32/64位引用),但序列化格式始终相同。

例如

在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[] ,然后每个字符两个字节,对齐到8字节边界…

什么标志可以改变大小?

如果您有64位引用,则char[]引用长度为8个字节,从而产生4个字节的填充。 如果您有64位JVM,则可以使用+XX:+UseCompressedOops来使用32位引用。 (所以单看JVM位大小并不能告诉你它的引用大小)

如果你有-XX:+UseCompressedStrings ,JVM将-XX:+UseCompressedStrings使用byte []而不是char数组。 这可能会略微减慢您的应用程序速度,但可以显着提高您的内存消耗 当使用byte []时,消耗的内存为每个字符1个字节。 ;)注意:对于4-char字符串,如示例所示,由于8字节边界,使用的大小相同。

“尺寸”是什么意思?

正如已经指出的那样,HashMap和List更复杂,因为很多(如果不是全部)Strings可以重用,可能是String文字。 你所说的“大小”取决于它的使用方式。 即结构单独使用多少内存? 如果结构被丢弃,将释放多少? 如果复制结构,将使用多少内存? 这些问题可以有不同的答案。

没有探查器你能做什么?

如果你可以确定可能的保守尺寸,足够小,确切的大小无关紧要。 保守的情况很可能是从头开始构造每个String和条目的地方。 (我只是说HashMap可能有10亿个条目的容量,即使它是空的。带有单个字符串的字符串可以是具有20亿个字符的字符串的子字符串)

您可以执行System.gc(),获取空闲内存,创建对象,执行另一个System.gc()并查看可用内存减少了多少。 您可能需要多次创建对象并取平均值。 多次重复这个练习,但它可以给你一个公平的想法。

(顺便说一句,虽然System.gc()只是一个提示,但Sun / Oracle JVM默认情况下每次都会执行Full GC)

我认为应该澄清这个问题,因为HashMap的大小和HashMap的大小以及HashMap包含的对象之间存在差异。

如果考虑HashMap的大小,在您提供的示例中,HashMap存储对String“aby”的一个引用和对List的一个引用。 所以列表中的多个元素无关紧要。 只有对列表的引用存储在值中。

在32位JVM中,在一个Map条目中,“aby”引用有4个字节,List引用有4个字节,Map条目的“hashcode”int属性为4个字节,“next”属性为4个字节地图条目。

您还添加了4 *(X-1)字节引用,其中“X”是HashMap在调用构造函数new HashMap>()时创建的空桶数。 根据http://docs.oracle.com/javase/6/docs/api/java/util/HashMap.html ,它应该是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被广泛使用,在某些情况下,应该值得使用具有初始容量和负载因子的构造函数。

如果不知道所有字符串是什么,以及每个列表中有多少项,或者不知道字符串是否都是唯一引用,则无法提前知道。

确切知道的唯一方法是将整个事物序列化为字节数组(或临时文件),并查看确切的字节数。