【发布时间】:2014-04-04 21:23:51
【问题描述】:
我最近遇到了这个article,它很好地介绍了内存映射文件以及如何在两个进程之间共享它。这是读取文件的进程的代码:
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 的任何调用都可能使用真正的空缓冲区(用零填充)来扩展文件,或者作者可能刚刚扩展了文件,但还没有写入任何内容......
【问题讨论】:
-
每当我看到“写入”和“共享数据”时,我认为需要同步。
-
我不知道你所说的“是否有同步的原因”是什么意思,但是打开大量内存映射文件,或者多次打开同一个文件,无论如何都是一个非常糟糕的主意,因为垃圾收集的原因,因为没有明确定义的时间可以释放相关内存。并且像 8k 这样的少量映射并没有特别的优势:您也可以只使用缓冲流,默认情况下具有那么多缓冲,并且没有关于文件扩展时要做什么的问题。映射文件最好用于极少数(例如一个)非常大的文件。
-
好的,知道了 - 打开一个大文件。尽管如此,这仍然是 IPC 的平均值,所以我想知道如何实现这一点,即一个进程写入,另一个进程读取,但在某种程度上,我们知道另一个进程在我们读取它之前实际上已经写了一些东西.这就是我说的同步
-
这是一个文件而不是管道。单独使用 mmap() 将不允许您进行同步。示例代码会(丑陋的)忙轮询。
标签: java buffer nio channel memory-mapped-files