我想使用HashSet<Long>
在内存中存储大量唯一数字。我计算了要消耗的近似内存(64位指针大小):
长将占用16个字节的空间。所以最初我将条目数乘以16得到内存。但实际上,每个条目的内存大大超过16个字节。之后我研究了HashSet
实施。简而言之,在底层实现中,它实际上为 hashset 的每个条目存储了一个额外的虚拟对象(12个字节)。并指向下一个条目的指针(8个字节)。因此,每个条目承认额外的12 + 8字节。
每个条目的总内存:16 + 12 + 8 = 36个字节。但是当我运行代码并检查内存时,每个条目仍然超过36个字节。
我的问题(简而言之):HashSet
占用了多少内存(例如,在64位计算机上)?
答案 0 :(得分:5)
您可以使用此测试准确测量此尺寸:
long m1 = Runtime.getRuntime().freeMemory();
// create object (s) here
long m2 = Runtime.getRuntime().freeMemory();
System.out.println(m1 - m2);
使用-XX运行:-UseTLAB选项
在我的64位HotSpot上,空HashSet需要480个字节。
为什么这么多?因为HashSet具有复杂的结构(在调试模式下的btw IDE有助于查看实际字段)。它基于HashMap(适配器模式)。所以HashSet本身包含对HashMap的引用。 HashMap包含8个字段。实际数据位于节点数组中。 Node有:int hash; K键; V值;节点接下来。 HashSet仅使用键并将虚拟对象放入值中。
答案 1 :(得分:4)
对象的大小是一个实现细节。不能保证如果它在一个平台上是x个字节,在另一个平台上它也是x个字节。
如您所知, Long
已装箱,但16个字节错误。原始long
占用8个字节,但long
周围的框的大小取决于实现。根据{{3}}开销词和填充意味着一个盒装的4字节int
可以达到24个字节!
该(特定于热点)答案中提到的字节对齐和填充也适用于Entry
对象,这也会推动消耗。
答案 2 :(得分:1)
使用的内存是32 * SIZE + 4 * CAPACITY +(16 * SIZE)beign&#34; SIZE&#34;元素的数量。
答案 3 :(得分:1)
HashMap默认大小为16个HashMapEntry条目。 每个HashMapEntry上都有四个对象(int keyHash,Object next,Object key,Object value)。因此,它通过包装元素来引入空条目的开销。 此外,hashmap的扩展速率为2x,因此对于17个元素,您将有32个条目,其中15个为空。
更简单的方法是使用内存分析器检查堆转储。
答案 4 :(得分:1)
HashSet
是一个复杂的野兽。在审查了一些评论之后,这里有一些消耗内存的项目,你还没有考虑到:
long
原语被装入java.lang.Long
对象,添加到HashSet. Somebody mentioned that a
Long`对象的引用将是24个字节。加上引用,即8个字节。ArrayList
或LinkedList
等,但由于哈希算法可能会产生冲突,因此必须将HashSet
的元素放入集合中,由哈希码组织。最好的情况是只有1个元素的ArrayList
:您的Long
对象。 ArrayList
的默认后备数组大小为10,因此在对象中有10个对象引用,因此每Long
个至少有80个字节。由于Long
是一个整数,我怀疑散列算法可以很好地解决问题。我不确定其值超过Integer.MAX_VALUE的long会发生什么。由于生日悖论,这将不得不以某种方式发生碰撞。HashSet
基本上是HashMap
,其中值不重要。在引擎盖下,它创建了一个HashMap
,其中有一个桶数组来表示哈希表。数组大小基于容量,根据您添加的元素数量不明确。长话短说,哈希表是一种内存密集型数据结构。这是空间/时间的权衡。假设有一个良好的散列分布,你会得到恒定的时间查找,但需要额外的内存使用。