【问题标题】:Calculating HashMap overhead in Java在 Java 中计算 HashMap 开销
【发布时间】:2012-07-18 21:58:53
【问题描述】:

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

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

  1. 它是否足够重要,例如,如果内部数据约为 256mb,那么开销会很重要吗?
  2. 是否有可靠的方法(除了我发现在某些情况下不可靠的分析器)以数学方式计算其开销应该是多少

我对 hashmap 的总大小不感兴趣 - 只有使用 hashmap 会产生的开销。例如,如果我有 10 个整数,它们是 4 个字节,所以是 40 个字节。如果我将它们放在一个数组中,我会得到 12 个字节的恒定开销 - 对象标头为 8 个字节,长度为 4 个字节。如果我将它们放在另一个结构(例如 TreeSet)中,我的开销将不会是恒定的,因为树需要节点 - 所以我可能会得到一个用 n 表示的开销,其中 n 是集合中的项目数。

有几件事对我来说是显而易见的,我将在此将其作为我的起点。

  1. 我需要存储至少 1000 条多头。这些是可为空的类型,因此它们实际上是对象。因此,我假设正在使用的 8 字节长整数也有一个 8 字节的对象头。我将添加 16n 的因数。
  2. 我还需要对每个对象的引用,无论该对象是否已从地图中调用并正在使用,这些引用都必须存在;所以这是每个对象额外的 8 个字节。我们可以将其计入数据大小,但由于引用在 hashmap 本身中,我觉得最好将它们作为开销的一部分。我的逻辑如下:如果我从 hashmap 中取出所有数据并将其存储在变量中,那么这些 n 引用仍将存在于 hashmap 中,前提是我没有删除这些数据对象,我不会这样做。对象集是不变的,尽管它们可能会使用不同的键被回收。
  3. hashmap 本身有 8 个字节的开销。
  4. hashmap必须存储里面的项目数(或者我认为是这样!),所以这是 4 个字节。
  5. 我会假设散列键在一个数组中,按散列键顺序排序。数组有 12 个字节。
  6. 我也会无知地假设对象位于匹配的数组中,当它找到键时它会取消引用。我猜还有 12 个字节。

这给了我一个多项式方程:36 + 24n

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


第二个有效的问题是,JVM 与 JVM 之间的差异有多大?有没有任何独立于JVM的方法来解决它?为了举例说明我的意思,考虑一个只有 32 位对象标头的 JVM - 当查看数组时,您可能会说,即使大小因 JVM 不同而异,但可以公平地估计数组的开销将变为 8 个字节而不是12 在这种情况下。

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


我可以尝试阅读源代码或运行分析,但这可能会根据我的 JVM 产生误导性结果。我正在寻求你的帮助——也许是知道的人——提供一些我们都不知道的信息。谢谢!


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

每个条目 8 个字,每个 long 加上 8 个字节,加上 hashmap 对象标头的 8 个字节。

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

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

所以它似乎低于 100kbytes。

【问题讨论】:

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


【解决方案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

【讨论】:

  • 所以,澄清一下:对于我的直接哈希图,每个键/值对大约是 8 个字(32 位 32 字节,64 位 64 字节)。我假设这不包括密钥本身的大小或数据本身。
  • 感谢您的直截了当的回答!它还包括一些额外的信息(感谢博主,他也解释了 GC 的开销,我忘记了。)
【解决方案2】:

创建一个程序,在其中创建所有对象并将它们存储在一个简单的数组中。测量使用的内存(见Runtime)。

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

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

【讨论】:

  • 不...如果我愿意,我可以这样做,但我要求的是数学。
  • 对于数学,您需要准确了解对象在映射中的存储方式,准确了解引用、数组等在您的 JVM 上的成本,并执行您在问题中所做的事情.阅读 HashMap 的源代码和你的 JVM 的文档。
  • 这两件事我都可以做,但这两件事都不一定能回答这个问题。 -- 毕竟,我对源代码的阅读可能是错误的,显然 my JVM 的文档可能不适用于使用我的程序的其他人的 JVM。
  • 这就是为什么最好的方法是在两个 JVM 上进行测量。 64 位 JVM 消耗的内存与 32 位 JVM 不同。如果你不想做数学,也不想测量,还剩下什么?
  • 因为我需要知道实际的计算。根据我的第一个猜测,JVM 的差异并不重要——即使内存使用量翻倍也没有任何意义。如果有人有,我需要数学,而不是'rtfm'。 -- 考虑 O(n^2) 开销会很大,但 O(n) 不会,除非常数很大。如果有人真的知道内部发生了什么,那么将我的头靠在我的 JVM 上收集这些信息是没有意义的。你呢?
【解决方案3】:
  1. 它是否足够重要,例如,如果内部的数据大约为 256mb,那么开销会很重要吗?

绝对不是。 HashMap 中的 1000 个对象的开销在任何情况下都不值得担心:如果它们总共是 256mb,那就更少了。如果开销是 256k,而事实并非如此,那只会是 1%。不重要。

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

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

【讨论】:

  • 虽然我已经收到了一种合理的估计方法——但还是感谢您的回答!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多