我正在尝试在Java中使用非常大的方形矩阵,大约为n = 1e6或更多。矩阵不是稀疏的,所以我没有看到很多方法将它们表示为2D数组,这需要n ^ 2 * sizeof(int)位的内存。显然,即使添加编译器标志以用作我的机器允许的大堆,我也会遇到堆溢出错误。
我愿意为了这个问题而假设我拥有完美的计算机(无限制的RAM等),尽管实际上我是在64位机器上使用16演出的RAM。似乎我的机器只是如此相关,因为我受JVM限制而不是我的实际硬件(因为JVM不能有更多内存而不是我的物理机器。)
我理解(并引用,例如,这里Making a very large Java array)Java数组在理论上甚至不能比用于索引的MAX_INT大。
我的问题是:有没有办法从JVM堆中哄骗额外的内存
据我所知,如果有,他们可能不会给我更多信息。
例如
在C中,我可以声明静态常量变量,并将它们移动到代码的数据部分,该部分将具有比堆更多的空间,远远超过堆栈(Where are static variables stored (in C/C++)?)。
在Java中,似乎即使我将变量复制到“数据”部分,该值也会进入主堆 static allocation in java - heap, stack and permanent generation这意味着我已经成功地从堆中移出了一整个字节(是的!)
我的解决方案
我的“解决方案”并不是真正的解决方案。我创建了一个简单的数据结构,它使用RandomFileAccess io过程来替换对外部文件的读写的数组访问。它仍然是恒定时间访问,但我们从Java最快的操作之一转到非常非常慢的过程(尽管我们可以同时从文件中提取“缓存”行,这使得该过程非常快速)。 更好的想法?
不是我的问题
我不是问如何在java的最大数组大小之上创建一个数组。这是不可能的。这些是嵌套数组 - 单个n大小的数组很好,其中n个会导致问题。
我不是问这个How to deal with "java.lang.OutOfMemoryError: Java heap space" error (64MB heap size)。垃圾收集是不相关的 - 我甚至无法让阵列更加担心何时被删除。
我也不能使用迭代器(我认为),否则这是可能的;像矩阵乘法这样的函数需要能够直接索引
注意:Java不是在非常大的矩阵上进行操作的正确语言。我最好使用算盘。但我在这里,这是我无法控制的。
答案 0 :(得分:3)
您的原始问题有一些缺失的方面;例如,我无法相信你必须使用如此大的矩阵并且只是在运行之间“忘记它们”。好吧,也许你这样做,我不知道。
无论如何:你对RandomAccessFile
的使用是,imho,几乎就在那里;只有我是你,我才会使用FileChannel.map()
。在Unix系统上,它基本上是一种调用mmap(2)
的方法。在下面的场景中,我假设你的矩阵有一个FileChannel
(我认为你理解我的意思)。
由于您使用矩阵,因为看起来矩阵中任何给定“坐标”的值都具有相同的长度,这意味着您可以轻松计算文件中的偏移量以读取和/或写入给定值进入矩阵。当然,您不希望映射那个值,而是一个包含该值的窗口;使窗口大到足以使用,并且不要担心堆空间消耗: FileChannel.map()
不占用堆空间(除了对象簿记)。在64位JVM上,您无需担心;如果你使用的是32位JVM,你必须考虑地址空间的耗尽。
当然,存在过期问题:您需要多长时间才能保持活动状态。这完全取决于您的程序以及您使用它做什么。但是使用FileChannel
并映射相关区域是可行的方法。但是,应该提醒您,映射超过2 ^ 31 - 1个字节是不安全的;例如,解决2 ^ 30(1 GiB)字节窗口;并提醒您可以将ByteBuffer
转换为IntBuffer
s。
编辑:一些相关链接: