【发布时间】:2012-07-18 21:58:53
【问题描述】:
假设我在哈希图中存储了 1000 个对象。这个 hashmap 被扩展为允许我将三维坐标映射到存储在其中的对象;里面的对象有固定的大小。哈希键是一个长整数。
我将如何(从数学上)计算出这种结构的可能开销?
- 它是否足够重要,例如,如果内部数据约为 256mb,那么开销会很重要吗?
- 是否有可靠的方法(除了我发现在某些情况下不可靠的分析器)以数学方式计算其开销应该是多少?
我对 hashmap 的总大小不感兴趣 - 只有使用 hashmap 会产生的开销。例如,如果我有 10 个整数,它们是 4 个字节,所以是 40 个字节。如果我将它们放在一个数组中,我会得到 12 个字节的恒定开销 - 对象标头为 8 个字节,长度为 4 个字节。如果我将它们放在另一个结构(例如 TreeSet)中,我的开销将不会是恒定的,因为树需要节点 - 所以我可能会得到一个用 n 表示的开销,其中 n 是集合中的项目数。
有几件事对我来说是显而易见的,我将在此将其作为我的起点。
- 我需要存储至少 1000 条多头。这些是可为空的类型,因此它们实际上是对象。因此,我假设正在使用的 8 字节长整数也有一个 8 字节的对象头。我将添加 16n 的因数。
- 我还需要对每个对象的引用,无论该对象是否已从地图中调用并正在使用,这些引用都必须存在;所以这是每个对象额外的 8 个字节。我们可以将其计入数据大小,但由于引用在 hashmap 本身中,我觉得最好将它们作为开销的一部分。我的逻辑如下:如果我从 hashmap 中取出所有数据并将其存储在变量中,那么这些 n 引用仍将存在于 hashmap 中,前提是我没有删除这些数据对象,我不会这样做。对象集是不变的,尽管它们可能会使用不同的键被回收。
- hashmap 本身有 8 个字节的开销。
- hashmap必须存储里面的项目数(或者我认为是这样!),所以这是 4 个字节。
- 我会假设散列键在一个数组中,按散列键顺序排序。数组有 12 个字节。
- 我也会无知地假设对象位于匹配的数组中,当它找到键时它会取消引用。我猜还有 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。
【问题讨论】:
-
我会放弃第 1 点,因为我不认为 hashmap 存储实际的键。它计算它们的哈希并将其用作哈希图中的索引(可能存储为数组),我会将 n *(哈希码的大小)添加到您的公式中
-
似乎可行。尽管如此,这仍然表明一个未知大小的数组必须保持“稀疏”,因此没有键冲突。
-
Java 的 HashMap 不使用分离链吗?我相当确定您必须存储对密钥的引用。
-
@Razvan 它存储密钥,因为可能会发生冲突。 docs.oracle.com/javase/6/docs/api/java/util/Map.html#keySet()
-
是的,密钥也被存储了。我在谈论一个理论上的实现:)。 @RiverC:这两个可能会有所帮助:docs.oracle.com/javase/6/docs/api/java/util/…docs.oracle.com/javase/6/docs/api/java/lang/…
标签: java memory-management data-structures hashmap overhead