内存处理大型HashMap的高效方法

时间:2016-07-06 19:14:19

标签: java memory-management hashmap

我的项目正在处理正在写入excel文件的大量数据。我将此数据以while(dataChunk)的形式存储在静态HashMap中,其中列表的大小仅为3.然而,Map中的条目数可以在0到11,300之间。

这个项目的流程是:

  • 使用条目加载地图

  • 迭代地图并做点什么

  • 清除下一组条目的地图

我最近发现的关于HashMap的内容是它在违反设置大小时重新调整大小的方式。因此,不仅我的地图不断地重新调整大小,而且当我清除最大的条目时,它可能会有大约20,000个空条目。

所以我试图对这件事进行微观优化,而我却陷入了如何做到这一点的两难境地。我的两个想法是:

  1. 将初始HashMap的默认值设置为允许其最多只重新调整一次的值

  2. 使用每个新条目集的平均大小重新初始化HashMap以限制重新调整大小并允许垃圾收集器进行一些清理

  3. 我的直觉告诉我,选项二可能是最合理的选项,但是根据下一个输入集,这仍然可以证明大量的重新调整大小。但是,选项一大大限制了重新调整大小到一次操作,但随后留下了数以千计的空条目。

    我提出的两个解决方案中哪一个比另一个更好,两者之间的内存改进没有太大区别,或者我可以监控其他解决方案(不涉及更改数据结构)?

    编辑:仅仅针对某些情况,我想要这样做是因为偶尔项目耗尽堆内存并且我试图确定这个巨大的地图有多大影响。

    EDIT2:只是为了澄清,地图本身的大小是更大的值。密钥大小(即列表)仅为3。

2 个答案:

答案 0 :(得分:2)

我做了一些研究,结束了这个页面:How does a HashMap work in Java

第二个标题与调整开销大小有关,说明HashMap的默认值是size 16factorLoad 0.75

您可以在初始化时更改这些值,因此size 11300factorLoad为1,这意味着在您达到最大值之前,地图的大小不会增加,在你的情况下,据我所知,将永远不会。

我使用以下代码进行了快速实验:

public static void main(String[] args) throws Exception {
    Map<String, Integer> map = new HashMap<>(11000000, 1);
    //        Map<String, Integer> map = new HashMap<>();
    for (int i = 0; i < 11000000; i++) {
        map.put(i + "", i);
    }
    System.out.println(map.size());
    Thread.sleep(9000);
}

交换两个Map初始化,然后检查Task Manager中消耗的内存。

设置初始大小和factorLoad后,它使用~1.45GB内存。 如果没有设置值,它将使用~1.87GB内存。

每次重新初始化Map而不是将其清除为可能更小的Map取代它将会更慢,但您可能会暂时获得更多内存。

你也可以做到这两点。如果您知道每个周期的factorLoad个对象的数量,请重新初始化以设置初始大小和List属性。

该文章还建议Java 8 HashMap虽然可能更快,但也可能比Java 7中有更多的内存开销。可能值得尝试在两个版本中编译程序并查看哪个提供了改进内存解决方案如果没别的话会很有趣。

答案 1 :(得分:2)

这里的问题和已接受的回答太错误了,我不得不回答。

  

我有一个正在处理大量数据的项目   写入Excel文件。我将此数据存储在静态HashMap中   形式为Map,Integer>,其中列表的大小仅为   曾经3.地图中的条目数可以在任何地方   从0到11,300。

请不要误会我的意思,但这是微小 !!!甚至不必费心去优化这样的东西!我很快进行了一次测试,将哈希表中的“ 11300”个元素填充了不到12毫秒。

  

我最近发现的有关HashMap的内容是>违反设置的大小时如何重新调整其大小。因此,不仅我的地图会不断以惊人的长度调整大小,而且在我清除最大的一组
时,它可能>约有20,000个空条目   条目。

...只是要清楚。空条目几乎不占用空间,这些只是空指针。在64位计算机上,每个插槽8个字节,在32位计算机上,每个插槽4个字节。这里我们最多只讨论几千字节。

  

使用每个新条目集的平均大小重新初始化HashMap,以限制重新调整大小并允许垃圾收集器进行清理。

这不是条目的平均“大小”,而是预期的条目的平均数量。

  

编辑:仅在某些情况下,我想这样做是因为   有时项目用完了堆内存,而我试图   确定此巨大地图的影响程度是多少。

不太可能是地图。使用探查器!您可以毫不费力地存储数百万个元素。


接受的答案是错误的

  

您可以在初始化时更改这些值,因此大小为11300   的factorLoad为1,表示地图的大小直到   您已经达到了您的最高要求,据我所知,   永远不会。

这不是一个好建议。使用与插入的预期项目数相同的容量以及“一”的加载因子,您必然会发生大量的哈希冲突。这将是一场性能灾难。


结论

如果您不了解东西的工作原理,请不要尝试进行微优化。