当前位置: 首页 > 面试题库 >

Java NIO-内存映射文件

蒋硕
2023-03-14
问题内容

最近,我碰到了这篇文章,这篇文章很好地介绍了内存映射文件以及如何在两个进程之间共享它。这是读取文件的过程的代码:

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

public class MemoryMapReader {

 /**
  * @param args
  * @throws IOException 
  * @throws FileNotFoundException 
  * @throws InterruptedException 
  */
 public static void main(String[] args) throws FileNotFoundException, IOException, InterruptedException {

  FileChannel fc = new RandomAccessFile(new File("c:/tmp/mapped.txt"), "rw").getChannel();

  long bufferSize=8*1000;
  MappedByteBuffer mem = fc.map(FileChannel.MapMode.READ_ONLY, 0, bufferSize);
  long oldSize=fc.size();

  long currentPos = 0;
  long xx=currentPos;

  long startTime = System.currentTimeMillis();
  long lastValue=-1;
  for(;;)
  {

   while(mem.hasRemaining())
   {
    lastValue=mem.getLong();
    currentPos +=8;
   }
   if(currentPos < oldSize)
   {

    xx = xx + mem.position();
    mem = fc.map(FileChannel.MapMode.READ_ONLY,xx, bufferSize);
    continue;   
   }
   else
   {
     long end = System.currentTimeMillis();
     long tot = end-startTime;
     System.out.println(String.format("Last Value Read %s , Time(ms) %s ",lastValue, tot));
     System.out.println("Waiting for message");
     while(true)
     {
      long newSize=fc.size();
      if(newSize>oldSize)
      {
       oldSize = newSize;
       xx = xx + mem.position();
       mem = fc.map(FileChannel.MapMode.READ_ONLY,xx , oldSize-xx);
       System.out.println("Got some data");
       break;
      }
     }   
   }

  }

 }

}

但是,我对这种方法有几点评论/问题:

如果我们仅对空文件执行读取器,即运行

  long bufferSize=8*1000;
  MappedByteBuffer mem = fc.map(FileChannel.MapMode.READ_ONLY, 0, bufferSize);
  long oldSize=fc.size();

这将分配8000个字节,现在将扩展文件。返回的缓冲区的限制为8000,位置为0,因此,读取器可以继续读取空数据。发生这种情况后,阅读器将停止,如currentPos == oldSize

现在应该是作家了(代码被省去了,因为它很简单,可以从网站上引用)-它使用相同的缓冲区大小,因此它将先写入8000个字节,然后再分配8000个字节,以扩展文件。现在,如果我们假设此过程在此时暂停,然后返回到读取器,那么读取器将看到文件的新大小并分配剩余的文件(因此从位置8000到1600),然后再次开始读取,然后再读取垃圾…

我有点困惑是否有一个为什么要同步这两个操作。据我所知,任何对的调用都map可能会使用一个真正的空缓冲区(用零填充)扩展文件,或者编写器可能刚刚扩展了文件,但尚未向其中写入任何内容…


问题答案:

有几种方法。

  1. 让作者Lock在尚未写入的区域上获得排他性。写完所有内容后,释放锁。这与该系统上运行的所有其他应用程序兼容,但是除非您将读取器与其他方法之一结合使用,否则它要求读取器足够聪明以重试失败的读取

  2. 使用另一个通信通道,例如管道或套接字或文件的元数据通道,以使作者可以将完成的写入告知读者。

  3. 在文件中的某个位置写一个特殊的标记(作为协议的一部分),以说明所写的数据,例如

        MappedByteBuffer bb;
    …
    // write your data

    bb.force();// ensure completion of all writes
    bb.put(specialPosition, specialMarkerValue);
    bb.force();// ensure visibility of the marker


 类似资料:
  • 读 # mmap_read.py import mmap with open('lorem.txt', 'r') as f: with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as m: print('First 10 bytes via read :', m.read(10

  • 系统调用在调用进程的虚拟地址空间中提供映射,将文件或设备映射到内存中。 下面是两种类型 - 文件映射或文件支持的映射 - 此映射将进程的虚拟内存区域映射到文件。 这意味着读取或写入这些内存区域会导致文件被读取或写入。这是默认的映射类型。 匿名映射 - 此映射映射进程的虚拟内存区域,不受任何文件的支持。 内容被初始化为零。 这种映射类似于动态内存分配(malloc()),在某些实现中用于某些分配。

  • 问题内容: 我一直在尝试编写一些非常快速的Java代码,这些代码必须执行很多I / O。我正在使用返回ByteBuffer的内存映射文件: 我遇到的问题是ByteBuffer .array()方法(应返回一个byte []数组)不适用于只读文件。我想编写我的代码,以便它可以与构造在内存中的内存缓冲区和从磁盘读取的缓冲区一起使用。但是我不想将所有缓冲区都包装为ByteBuffer.wrap()函数,

  • 我想写一个字节向量,

  • 问题 你想内存映射一个二进制文件到一个可变字节数组中,目的可能是为了随机访问它的内容或者是原地做些修改。 解决方案 使用 mmap 模块来内存映射文件。 下面是一个工具函数,向你演示了如何打开一个文件并以一种便捷方式内存映射这个文件。 import os import mmap def memory_map(filename, access=mmap.ACCESS_WRITE): siz

  • 问题内容: 我用np.save()保存了几个numpy数组,并将它们放在一起非常大。 是否可以将它们全部加载为内存映射文件,然后对它们进行串联和切片,而无需将任何内容都加载到内存中? 问题答案: 使用显然将数组加载到内存中。为避免这种情况,您可以轻松地在新文件中创建一个thrid数组,并从要连接的数组中读取值。以更有效的方式,您还可以将新阵列追加到磁盘上已存在的文件中。 在任何情况下,您都必须为数