我该如何处理Java中的一个非常大的数组?

时间:2009-12-16 22:46:09

标签: java nio

我有一个算法,它目前分配一个非常大的双精度数组,它经常更新和搜索。数组的大小为N ^ 2/2,其中N是算法运行的行数。我还必须保留整个事物的副本,以便与围绕算法的应用程序相关联。

当然,这限制了我的算法可以处理的行数,因为我有堆限制要与之抗衡。到目前为止,我已经离开了要求使用该算法的人更新-Xmx设置以分配更多空间,并且这一切正常。但是,我现在有一个真正的问题,我需要这个数组大于我可以适应内存。

我已经计划改变我的算法以减轻这个大型阵列的必要性,并在该域中获得一些有希望的结果。然而,这是对该过程的根本性改变,并且在它达到我当前代码的高度抛光状态之前需要更多的工作,该代码在生产中非常成功地运行并且已经存在了几年。

因此,当我完善我的新算法时,我想延长现有算法的寿命,这意味着解决与分配我庞大的双精度数组相关的堆限制。

我的问题是处理它的最佳方法是什么?我应该使用nio FileChannel和MappedByteBuffer,还是有更好的方法。如果我使用nio方法,与相同大小的内存数组相比,我应该采取什么样的性能?

由于

7 个答案:

答案 0 :(得分:6)

如果您开始耗尽可用内存,那么您可能很快就会开始耗尽可用的数组索引,数组的大小限制为Integer.MAX_VALUE,并且当使用双精度数组作为数组时元素“仅”32GB大小。

获得具有32GB内存的计算机是昂贵的,但可能没有您修改算法和所有相关测试的时间那么昂贵。

但是,如果客户端正在运行到内存边缘,并且它们的数据集仍在增长,那么现在咬你的子弹是有意义的,并使更改能够在任何给定时间使用更少的内存因为无论如何它们可能很快就会超出阵列。

假设数组稀疏地填充,您拥有的另一个选项是使用各种稀疏数组数据结构中的一种,尽管如果您的数组小于20%,这些选项往往是有益的。

编辑:由于您似乎已经调查了替代方案,因此MappedByteBuffer可能就是您的选择。显然这会对性能产生影响,但是如果你主要从数组进行顺序读写操作,那么这应该不会太糟糕。如果您正在进行随机读写操作,那么这将非常快速地变慢。或者非常缓慢......取决于你如何看待这些东西; - )

答案 1 :(得分:2)

如果你在PC上运行,映射文件的页面大小可能是4千字节。

所以这个问题真的从我开始将数据交换到磁盘开始,“我随机访问RAM是多么随机,现在是一个文件”?

并且(...我可以,如果是这样......)我如何订购双打以最大化在一起访问4K页面中的双打而不是在下一个4K磁盘之前的每一页中一次访问的情况取?

如果您使用标准IO,您可能仍然希望以块的形式进行读写,但其中的块可能会更小。扇区将至少为512字节,磁盘集群更大,但是如果每个IO都有内核往返开销,那么读取的最大大小是什么?

对不起,我担心你最好的后续步骤在很大程度上取决于你使用的算法和数据。

答案 2 :(得分:1)

我对使用Java的MappedByteBuffers有了很好的经验,并鼓励您深入了解它。它很可能会让您不再处理-Xmx更改。请注意,如果您需要超过2-4GB的可寻址空间,则需要64位CPU,OS和JVM。

要超越Integer.MAX_VALUE索引问题,您可以编写分页算法,就像我在Binary search in a sorted (memory-mapped ?) file in Java的相关答案中所做的那样。

答案 3 :(得分:0)

您正在进行如何编写利用缓存(如在cpu中的内存缓存中)的软件的领域。这很难做到,“正确”的方法取决于算法的设计方式。

那么,你的程序实际上在算法上做了什么?

答案 4 :(得分:0)

您可以尝试将数组存储为数据库表中的行,并使用存储过程对其进行更新和搜索。

另一个想法:

使用B-Tree作为阵列并在磁盘上保留一些叶子。确保B-Tree的节点大小为页面大小或多页大小。

答案 5 :(得分:0)

如果问题是内存不足,那么简单的解决方案是使用更多内存升级硬件,增加Java堆大小和/或切换到64-bi5t JVM。

另一方面,如果您正在针对数组大小的Java限制运行,则可以沿着ByteBuffer路由运行,或者可以切换到使用数组数组。后者是Sun建议的解决方法。

使用数组数组方法,您可以(理论上)处理N接近2**31的值。实际上,您的限制将取决于您拥有的物理内存量以及使用OS / JVM组合可以解决的数量。

答案 6 :(得分:0)

请注意,某些操作系统比其他操作系统更好地支持内存映射。

我很想做到这一点:

  1. 将所有数组get / puts放在一个对象接口后面(如果它们还没有),这样可以让你轻松更改实现。
  2. 使用SoftReferences数组,其中每个SoftReference指向该行的双精度数组。当GC启动时,使用ReferenceQueue将阵列保存到磁盘。当get()返回null时,从磁盘检索。
  3. 您可能会发现您可以通过这种方式更好地控制性能 - 可以根据需要调整-Xmx。