32位机器上的Java内存映射

4

我有一个大小为5GB的文件。我希望在Java中进行内存映射。我知道一个内存映射部分的大小不能超过2GB。

我的问题是,是否可以创建5个1GB的内存映射部分来映射完整的5GB文件,并在同一Java应用程序中访问它们。


2GB限制的原因是MAX_INT(您需要一个整数才能访问数组)。虽然我不理解动机,但您建议的听起来可行。 - Nir Alfasi
如果可能的话,将数据流式传输以进行处理。它需要一次性全部适应内存吗? - Steve Kuo
2个回答

1
不,这是不可能的。
这里有两个问题:
首先,32位机器(或64位机器上的32位操作系统)只有4 GB(32位)的地址空间,因此即使是从C语言开始你也无法一次性将5 GB文件映射到内存中。
另一个问题是Java内存映射的实现局限性,通过MappedByteBuffer进行处理。虽然方法FileChannel.map()采用long作为offset和size,但它返回的MappedByteBuffer仅能使用int作为其limit和position。这意味着,即使在64位机器和操作系统上,您可以将整个5 GB文件视为单个区域映射,在Java中,您仍需要手动创建一系列映射区域,每个区域不能大于2 GB。尽管如此,您仍至少可以按块映射5 GB,而在32位操作系统上,您无法同时拥有它们的映射。考虑到在Java中取消映射文件区域需要一些丑陋的技巧,为了保持它们在限制范围内,映射和取消映射区域是不方便的(尽管可能)。您可以查看Lucene或Cassandra的源代码。据我所知,他们还会在可能的情况下使用带有本机代码的库,以比纯Java更有效地处理映射和取消映射。
为了让事情变得更加复杂,2 GB是理论限制,由于内存碎片化,在32位操作系统上可能无法达到该限制。一些操作系统也可以配置3-1的内存分割,这样只剩下1 GB的地址空间可供用户空间程序使用,其余的则归属于操作系统地址空间。因此,在实践中,您应该尝试映射比2 GB小得多的块,您更有可能成功地映射250 MB的4-6个块,而不是映射单个2 GB的块。

你有一点错误。首先,有sun.nio.ch.FileChannelImpl类,其中包含私有方法map0和unmap0,没有2GB的限制,如果您想在x64上使用大型内存映射文件,则可以选择此选项。其次,没有任何阻止您创建超过4GB(总计)的内存映射文件,操作系统将通过交换机制处理它(但您会感到明显的性能下降),因此从技术上讲答案是“是的,您可以”。更重要的是,“是的,您可以使用一个内存映射部分来完成”。 - qwwdfsad
1
是的,您可以使用内部API,但这需要一些丑陋的技巧,并且至少在理论上可能会在任何下一个Java版本中出现问题。至于4 GB限制-不,您无法在32位操作系统的单个进程内同时映射超过4 GB。每个映射字节都有自己的地址,因此没有足够的地址空间来分配。这与数据是否将加载到RAM中无关。虚拟内存子系统中没有足够的地址来进行映射。 - Michał Kosmulski
好的,我误解了视图(作为文件的一部分)和整个文件本身,我错了 :) - qwwdfsad

0
请查看MappedByteBufferFileChannel.map()的Java文档。
我不是Java NIO方面的专家,所以我不确定字节缓冲区是否会自动处理块,或者您是否需要使用多个MappedByteBuffer。请随意编写一个简单的类来测试和操作您的大文件。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接