为什么mmap()(内存映射文件)比read()快

我最近正在研究Java NIO的MappedByteBuffer。 我已经阅读了一些关于它的文章,他们都提到“mmap()比read()更快”

在我的结论中:

  1. 我对待MappedByteBuffer ==内存映射文件== mmap()

  2. read()必须通过以下方式读取数据:磁盘文件 – >内核 – >应用程序,因此它具有上下文切换和缓冲区复制

  3. 他们都说mmap()比read()有更less的复制或系统调用,但是据我所知,第一次访问文件数据时也需要从磁盘文件中读取数据。 所以它第一次读取:虚拟地址 – >内存 – >页面错误 – >磁盘文件 – >内核 – >内存。 除了可以随意访问外,最后3步(磁盘文件 – >内核 – >内存)与read()完全相同,那么mmap()与read()的复制或系统调用的复制方式如何?

  4. mmap()和交换文件之间的关系是什么,os会把内存中最less使用的文件数据交换(LRU)? 所以当你第二次访问这些数据时,OS从交换中检索它们而不是磁盘文件(不需要拷贝到内核缓冲区),这就是为什么mmap()的拷贝和系统调用较less的原因?

  5. 在java中,MappedByteBuffer是从堆中分配的(这是一个直接的缓冲区)。 所以当你从MappedByteBuffer读取,这是否意味着它需要一个额外的内存复制从外部的Java堆到Java堆?

有谁能回答我的问题吗? 谢谢 :)

1:是的,这基本上是一个MappedByteBuffer。

2:“磁盘文件 – >内核”不一定涉及复制。

3:使用内存映射文件,一旦内核将文件读入缓存中,它就可以简单地将这部分缓存映射到进程中 – 而不必将缓存中的数据复制到进程指定的位置。

4:如果内核决定从内存映射文件中取出页面,则不会将页面写入页面文件; 它将在丢弃页面之前将页面写入原始文件(它映射的文件)。 将其写入页面文件将是不必要的,浪费页面文件空间。

5:是的。 例如,如果调用get(byte[])那么数据将从堆外映射复制到您的数组中。 请注意,诸如get(byte[])函数需要为任何类型的缓冲区复制数据 – 这不是特定于内存映射文件的。

你正在比较苹果和橘子。 mmap()read()快,因为它不会执行任何I / O操作。 当你访问映射产生的内存地址时,I / O被延迟。 这个 I / O和read(),很相似read(),不管I / O比read()快还是一个相当有意义的点。 我想在接受之前看到一个合适的基准。

我对待MappedByteBuffer ==内存映射文件== mmap()

好。

read()必须通过磁盘文件 – >内核 – >应用程序读取数据,所以它有两次上下文切换和缓冲区复制

比较什么?

他们都说mmap()比read(),有更少的复制或系统调用read(),

它有更少的系统调用。 是否有较少的复制取决于实施。 通过DMA直接读取和写入数据当然是可能的,但是特定的操作系统是否特定于操作系统。

但据我所知,第一次访问文件数据时也需要从磁盘文件中读取数据。

正确。

所以它第一次读取:虚拟地址 – >内存 – >页面错误 – >磁盘文件 – >内核 – >内存。 除了可以随意访问外,最后3步(磁盘文件 – >内核 – >内存)与read()完全相同,那么mmap()与read()的复制或系统调用的复制方式如何?

由于DMA,如果实施。

mmap()和交换文件之间有什么关系

分配给地图的内存是进程地址空间的一部分,它是虚拟的,并且可以交换,交换文件中必须有空间,就像任何其他内存一样。

这是操作系统会把内存最少使用的文件数据交换(LRU)?

没有。

所以当你第二次访问这些数据时,OS从交换中检索它们而不是磁盘文件(不需要拷贝到内核缓冲区),这就是为什么mmap()的拷贝和系统调用较少的原因?

不,要做所有这些都是错的。

在java中, MappedByteBuffer是从堆中分配的(这是一个直接的缓冲区)。

这没有意义。 直接缓冲区不是从堆中分配出来的,它们是由mmap()或任何平台API分配的,作为新的内存。 不在堆中。 你是正确的MappedByteBuffer是一个直接的缓冲区。

所以当你从MappedByteBuffer读取,这是否意味着它需要一个额外的内存复制从外部的Java堆到Java堆?

是的,但不是因为上述原因。 原因是你必须调用MappedByteBuffer.get()/put(),这本身就是一个额外的步骤。