在Java中计算HashMap开销

时间:2012-07-19 16:48:02

标签: java memory-management data-structures hashmap overhead

假设我在散列图中存储了1000个对象。这个hashmap被扩展为允许我将三维坐标映射到存储在其中的对象;里面的物体有固定的大小。散列键是一个长整数。

我如何计算(数学上)这种结构的可能开销?

  1. 是否足够重要,例如,如果内部数据大约为256mb,那么开销就很重要了?
  2. 是否有一种可靠的方法(除了我发现在某些情况下不可靠的分析器)以数学方式计算它的开销应该是什么
  3. 我对hashmap的总大小不感兴趣 - 只有使用hashmap的开销才会产生。例如,如果我有10个整数,那么它就是4个字节,所以它是40个字节。如果我把它们粘在一个数组中,我得到一个12字节的常量开销 - 对象头为8,长度为4。如果我将它们放在另一个结构(例如TreeSet)中,我的开销将不会保持不变,因为树需要节点 - 因此我可能会得到以n表示的开销,其中n是集合中的项目数。

    有些事情对我很明显,我将在此作为我的起点。

    1. 我需要储存至少1000条长肉。这些是可空类型,因此它们实际上是对象。因此,我将假设使用的8字节长整数具有8字节的对象头。我将添加一个16n的因子。
    2. 我还需要对每个对象的引用,无论对象是否已从地图中调用并且正在被使用,它都必须存在;这样每个对象额外增加8个字节。我们可以将其分解为数据大小,但由于引用位于hashmap本身,我觉得最好将它们作为开销的一部分。我的逻辑如下:如果我从hashmap中取出所有数据并将其存储在变量中,那么n个引用仍然存在于hashmap中,前提是我没有 remove 这些数据对象,我不会这样做的。这组对象是不变的,但它们可以使用不同的密钥进行回收。
    3. hashmap本身的开销为8个字节。
    4. hashmap 必须存储内部的项目数(或者我认为!),这样就是4个字节。
    5. 我会无知地认为哈希键在一个数组中,按哈希键顺序排序。这是阵列的12个字节。
    6. 我将无知地假设对象在匹配的数组中,当它找到键时它会取消引用。我猜另外12个字节。
    7. 这给出了一个多项式方程:36 + 24n

      因此,我猜测使用长密钥的1000个数据对象的开销为24036字节。这在某种程度上是一个微不足道的开销,但我的问题是,真正的开销是什么,只是坐在那里?


      一个有效的第二个问题是,这从JVM到JVM有多大差异?是否有任何独立于JVM的方法来解决它?为了举例说明我的意思,考虑一个只有32位对象头的JVM - 在查看你可能会说的数组时,即使从JVM到JVM的大小不同,可以合理地估计一个数组上的开销会变成8个字节而不是在那种情况下12。

      我假设在相同版本的Java上实现了HashMap的固定实现。


      我可以尝试阅读源代码或运行分析,但这可能会产生基于我的JVM的误导性结果。我正在寻求你的帮助 - 或许有人知道 - 我们都不知道有关情况的一些信息。谢谢!


      见下面的答案,实际估计可表示如下:

      每个条目8个字,每个长度加8个字节,加上hashmap对象标题的8个字节。

      在我目前的环境(32位操作系统)中,1个字= 4个字节。

      • 32位环境中的40n + 8:1000条目的约40k
      • 在64位环境中72n + 8:1000个条目~72k。

      所以它似乎低于100k字节。

3 个答案:

答案 0 :(得分:1)

以下blog post提供了一些关于该主题的松散数学 这个google code site可以看看这些事情是如何完成的。

在链接腐烂的情况下引用链接:

This is the cheat-sheet I compiled.

To compute the cost of a single (key, value) entry:

    If you use HashMap or ConcurrentHashMap, the cost is 8 words (32 bytes)


 So, consider this example from the javadoc:

   LoadingCache graphs = CacheBuilder.newBuilder()
       .maximumSize(10000)
       .expireAfterWrite(10, TimeUnit.MINUTES)
       .removalListener(MY_LISTENER)
       .build(
           new CacheLoader() {
             public Graph load(Key key) throws AnyException {
               return createExpensiveGraph(key);
             }
           });


The cost of an Entry in this structure this is computed as follows:

    It's a Cache: +12 words
    It uses maximumSize(): +4 words
    It uses expiration: +4 words

Thus, each (key, value) entry would have a footprint of 20 words (thus 80 bytes in a 32bit VM, or 160 in a 64bit one). 

To estimate the overhead imposed in the garbage collector, one could count how many references (pointers) each entry introduces, which the garbage collector would have to traverse to compute object reachability. The same list again, this time only counting references:

    If you use HashMap or ConcurrentHashMap, the cost is 5 references

答案 1 :(得分:0)

创建一个程序,您可以在其中创建所有对象并将它们存储在一个简单的数组中。测量已用内存(请参阅Runtime)。

然后将它们存储在HashMap中。测量已用内存。

将第一个测量的内存减去第二个使用过的内存,你就有了HashMap的开销。

答案 2 :(得分:0)

  
      
  1. 是否足够重要,例如,如果内部数据大约为256mb,那么开销就很重要了?
  2.   

绝对不是。在任何情况下,HashMap中1000个对象的开销甚至都不值得担心:如果它们总共为256mb,则更不用说。如果开销是256k,而不是,则只有1%。不重要。

  
      
  1. 是否有可靠的方法(除了我发现在某些情况下不可靠的分析器)以数学方式计算其开销应该是多少?
  2.   

鉴于我对(1)的回答,这个问题没有实际意义。