Java NIO MappedByteBuffer OutOfMemoryException

时间:2012-09-21 13:56:09

标签: java file-io nio bytebuffer filechannel

我遇到了麻烦:我希望使用FileChannelMappedByteBuffer来读取几GB的HUGE文件 - 我发现的所有文档都暗示使用{{映射文件相当简单1}}方法。 当然,由于所有缓冲区方法都使用int作为位置,限制和容量,因此限制为2GB - 但系统隐含的限制如何呢?

实际上,我遇到了很多关于FileChannel.map()的问题!并没有真正定义限制的文档! 那么 - 如何将一个符合int-limit的文件安全地映射到一个或多个OutOfMemoryException而不仅仅是例外?

在尝试MappedByteBuffer之前,我可以问系统我可以安全地映射文件的哪个部分?怎么样? 为什么关于这个功能的文档很少?

4 个答案:

答案 0 :(得分:8)

我可以提供一些有效的代码。这是否能解决您的问题很难说。通过文件搜索Hunter识别的模式。

请参阅优秀文章Java tip: How to read files quickly了解原始研究(不是我的)。

// 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();
}

答案 1 :(得分:6)

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

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

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

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

答案 2 :(得分:3)

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

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

  

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

答案 3 :(得分:2)

文件越大,你想要的内存就越少。设计一种方法,一次处理一个缓冲区,一次一行,等等。

MappedByteBuffers特别成问题,因为没有定义的映射内存版本,因此一次使用多个版本基本上会失败。