如何减少HashMap的内存使用量<string,integer =“”>类似于数据结构

时间:2017-04-14 02:11:23

标签: java performance hashmap

在开始解释我的问题之前,我应该提一下,我不是在寻找增加Java堆内存的方法。我应该严格存放这些物品。

我正在努力将大量(5-10 GB)的DNA序列及其计数(整数)存储在哈希表中。 DNA序列(长度为32或更短)由'A','C','G','T'和'N'(未定义)字符组成。众所周知,当在内存中存储大量对象时,与C和C ++等低级语言相比,Java的空间效率较差。因此,如果我将此序列存储为字符串(对于长度为30的序列,它保存大约100 MB内存),我会看到错误。

我试图将核酸表示为'A'= 00,'C'= 01,'G'= 10,'T'= 11并忽略'N'(因为它将char烧成2位变换为第5个酸)。然后,将这些2位酸连接成字节数组。它带来了一些改进但不幸的是我再次看到错误几个小时后。我需要一个方便的解决方案或至少一种解决方法来处理此错误。提前谢谢。

3 个答案:

答案 0 :(得分:2)

我建议您查看Trove4j Collections API;它提供了包含原语的集合,这些原语将比它们的盒装包装类使用更少的内存。

具体来说,您应该查看他们的TObjectIntHashMap

此外,我建议不要将Stringchar存储为JDK 9,因为char的后备String数组是UTF-16编码,每byte使用两个char s。 JDK 9默认为UTF-8,其中只使用一个byte

答案 1 :(得分:2)

相当复杂,这可能是一个奇怪的想法,需要做很多工作,但这就是我想要的:

您已经指出了整个任务的两个单独的子问题:

  • 对于如此大的集合大小,默认的HashMap实施可能不是最理想的
  • 你需要存储除字符串以外的东西

地图实施

我建议为Map<String, Long>接口编写一个高度定制的哈希映射实现。在内部,您不必存储字符串。不幸的是5 ^ 32&gt; 2 ^ 64,所以没有办法将整个字符串打包成一个长的,好吧,让我们坚持两个长的钥匙。在为地图实现提供字符串键时,可以相当有效地进行字符串/返回长[2]转换(使用位移等)。

关于打包值,以下是一些注意事项:

对于键值对,标准hashmap需要有一个N long数组用于存储桶,其中N是当前容量,当从散列键中找到存储桶时,它需要具有密钥链表-value对用于解析生成相同哈希码的密钥。对于您的具体情况,您可以尝试以下列方式对其进行优化:

  • 使用大小为3N的long [],其中N是以连续数组存储键和值的容量
  • 在此数组中,位置3 * (hashcode % N)3 * (hashcode % N) + 1存储密钥的长[2]表示,匹配此存储桶或唯一存储的第一个密钥(在插入时,否则为零),在位置3 * (hashcode % N) + 2存储相应的计数
  • 对于所有这些情况,其中不同的密钥导致相同的哈希码,从而产生相同的存储桶,您将数据存储在标准HashMap<Long2KeyWrapper, Long>中。我们的想法是保持上面提到的数组的容量(并相应地调整大小)足够大到目前为止该连续数组中的大部分数据而不是后备散列映射。这将大大减少hashmap的存储开销
  • 不要在N = 2N次迭代中扩展容量,做出较小的增长步骤,例如10-20%。这将在填充地图时降低成本,但会控制内存占用

鉴于不平等5^32 > 2^64你的想法使用位来编码5个字母似乎是我现在能想到的最好的。使用3位并相应地长[2]。

答案 2 :(得分:0)

如果您正在使用~10gb数据的顺序,或者至少使用内存表示大小为~10gb的数据,那么您可能需要考虑如何编写您不需要的数据。目前需要磁盘并将数据集的各个部分加载到内存中以对其进行处理。

几年前,当我使用蒙特卡罗模拟进行研究时,我遇到了这个问题,所以我写了Java data structure来解决它。您可以在此处克隆/分叉源代码:github.com/tylerparsons/surfdep

该库支持MySQL和SQLite作为底层数据库。如果您没有,我建议使用SQLite,因为它设置起来要快得多。

完全免责声明:这不是最有效的实现,但如果让它运行几个小时,它将处理非常大的数据集。我在Windows笔记本电脑上使用多达10亿个元素的矩阵成功测试了它。