我最近正在研究Java NIO的MappedByteBuffer。 我已经阅读了一些关于它的帖子,所有人都提到“mmap()比read()更快”(
在我的结论中:
我对待MappedByteBuffer ==内存映射文件== mmap()
read()必须通过以下方式读取数据:disk file - >内核 - >应用程序,因此它具有上下文切换和缓冲区复制
他们都说mmap()比read()拥有更少的复制或系统调用,但据我所知,它还需要在您第一次访问文件数据时从磁盘文件中读取。所以它第一次阅读:虚拟地址 - >记忆 - >页面错误 - >磁盘文件 - >内核 - >记忆。除了你可以随机访问它,最后3个步骤(磁盘文件 - >内核 - >内存)与read()完全相同,那么mmap()如何比read()更少复制或系统调用?
mmap()和交换文件之间的关系是什么,os是否会将最少使用的内存文件数据放入交换(LRU)?因此,当您第二次访问这些数据时,操作系统会从交换而不是磁盘文件中检索它们(无需复制到内核缓冲区),这就是为什么mmap()复制和系统调用较少的原因?
在java中,MappedByteBuffer是从堆中分配的(它是直接缓冲区)。因此,当您从MappedByteBuffer中读取时,是否意味着需要从Java堆外部再添加一个额外的内存副本到Java堆中?
有人能回答我的问题吗?谢谢:))
答案 0 :(得分:6)
1:是的,这基本上就是MappedByteBuffer。
2:“磁盘文件 - >内核”不一定涉及复制。
3:使用内存映射文件,一旦内核将文件读入其缓存,它就可以简单地将缓存的那部分映射到您的进程中 - 而不必将数据从缓存复制到您的位置进程指定。
4:如果内核决定从内存映射文件换出页面,它将不会将页面写入页面文件;在丢弃页面之前,它会将页面写入原始文件(它映射的文件)。将其写入页面文件是不必要的,浪费页面文件空间。
5:是的。例如,如果您调用get(byte[])
,则数据将从堆外映射复制到您的阵列中。请注意,get(byte[])
等函数需要复制任何类型缓冲区的数据 - 这不是特定于内存映射文件的。
答案 1 :(得分:1)
你在比较苹果和橘子。 mmap()
比read()
'快,因为它不执行任何I / O.当您访问由映射产生的内存地址时,I / O将延迟到。 那个 I / O与read(),
非常相似, I / O是否比read()
更快是一个非常有用的话题。在我接受之前,我希望看到一个合适的基准。
我会对待
MappedByteBuffer
==内存映射文件==mmap()
行。
read()
必须通过以下方式读取数据:磁盘文件 - >内核 - >应用程序,因此它有两次上下文切换和缓冲区复制
与什么相比?
他们都说mmap()比
拥有更少的复制或系统调用read(),
系统调用较少。它是否具有较少的复制取决于实现。当然可以通过DMA直接读取和写入数据,但是特定的操作系统是否是特定于操作系统的。
但据我所知,它还需要在您第一次访问文件数据时从磁盘文件中读取。
正确。
所以它第一次阅读:虚拟地址 - >记忆 - >页面错误 - >磁盘文件 - >内核 - >记忆。除了你可以随机访问它,最后3个步骤(磁盘文件 - >内核 - >内存)与read()完全相同,那么mmap()如何比read()更少复制或系统调用?
由于DMA,如果已实施。
mmap()和交换文件
之间的关系是什么
分配给地图的内存是进程地址空间的一部分,它是虚拟的,并且需要交换,并且交换文件中必须有空间,就像任何其他内存一样。
os是否会将最少使用的内存文件数据放入交换(LRU)?
没有
因此,当您第二次访问这些数据时,操作系统会从交换而不是磁盘文件中检索它们(无需复制到内核缓冲区),这就是为什么mmap()复制和系统调用较少的原因?
没有。做所有这些都是错误的。
在java中,
MappedByteBuffer
是从堆中分配的(它是直接缓冲区)。
这没有意义。直接缓冲区不会从堆中分配,它们由mmap()
或平台API分配,作为 new 内存。不在堆中。你是正确的MappedByteBuffer
是直接缓冲区。
因此,当您从MappedByteBuffer中读取时,是否意味着需要从Java堆外部再添加一个额外的内存副本到Java堆中?
是的,但不是出于上述原因。原因是你必须致电MappedByteBuffer.get()/put(),
,这本身就是一个额外的步骤。