6

我真的遇到了麻烦:我想使用FileChannels 和MappedByteBuffers 读取超过几 GB 的巨大文件 - 我发现的所有文档都表明使用该FileChannel.map()方法映射文件相当简单。当然,2GB 是有限制的,因为所有 Buffer 方法都使用 int 来表示位置、限制和容量——但是低于该限制的系统隐含限制呢?

实际上,我遇到了很多关于OutOfMemoryExceptions! 的问题!而且根本没有真正定义限制的文档!那么 - 我如何才能将适合 int-limit 的文件安全地映射到一个或几个MappedByteBuffers 而不会出现异常?

我可以在尝试之前询问系统我可以安全地映射文件的哪个部分FileChannel.map()吗?如何?为什么有关此功能的文档很少?

4

4 回答 4

9

我可以提供一些工作代码。这是否解决了你的问题很难说。这会在文件中寻找Hunter.

请参阅出色的文章Java 技巧:如何快速读取文件以进行原始研究(不是我的)。

// 4k buffer size.
static final int SIZE = 4 * 1024;
static byte[] buffer = new byte[SIZE];

// Fastest because a FileInputStream has an associated channel.
private static void ScanDataFile(Hunter p, FileInputStream f) throws FileNotFoundException, IOException {
  // Use a mapped and buffered stream for best speed.
  // See: http://nadeausoftware.com/articles/2008/02/java_tip_how_read_files_quickly
  FileChannel ch = f.getChannel();
  long red = 0L;
  do {
    long read = Math.min(Integer.MAX_VALUE, ch.size() - red);
    MappedByteBuffer mb = ch.map(FileChannel.MapMode.READ_ONLY, red, read);
    int nGet;
    while (mb.hasRemaining() && p.ok()) {
      nGet = Math.min(mb.remaining(), SIZE);
      mb.get(buffer, 0, nGet);
      for (int i = 0; i < nGet && p.ok(); i++) {
        p.check(buffer[i]);
      }
    }
    red += read;
  } while (red < ch.size() && p.ok());
  // Finish off.
  p.close();
  ch.close();
  f.close();
}
于 2012-09-21T14:05:36.450 回答
7

我使用的是List<ByteBuffer>每个 ByteBuffer 映射到 16 MB 到 1 GB 块中的文件。我使用 2 的幂来简化逻辑。我用它来映射高达 8 TB 的文件。

内存映射文件的一个关键限制是您受到虚拟内存的限制。如果您有一个 32 位 JVM,您将无法进行很多映射。

我不会继续为文件创建新的内存映射,因为这些映射永远不会被清理。您可以创建很多这些,但在某些系统上似乎有大约 32K 的限制(无论它们有多小)

我发现 MemoryMappedFiles 有用的主要原因是它们不需要被刷新(如果你可以假设操作系统不会死机)这允许你以低延迟的方式写入数据,而不必担心如果应用程序因不得不 write() 或 flush() 而死掉或性能过高。

于 2012-09-21T14:38:07.090 回答
3

您不使用FileChannelAPI 一次写入整个文件。相反,您将文件分部分发送。请参阅 Martin Thompson 比较 Java IO 技术性能的帖子中的示例代码:Java Sequential IO Performance

此外,没有太多文档,因为您正在进行依赖于平台的调用。来自map()JavaDoc:

内存映射文件的许多细节本质上依赖于底层操作系统,因此未指定。

于 2012-09-21T14:04:12.237 回答
2

文件越大,您就越不希望一次将其全部存储在内存中。设计一种方法来处理文件一次缓冲区,一次一行等。

MappedByteBuffers 尤其成问题,因为没有定义的映射内存释放,所以一次使用多个基本上肯定会失败。

于 2012-09-21T21:54:50.520 回答