我最近正在研究Java NIO的MappedByteBuffer。 我已经阅读了一些关于它的文章,他们都提到“mmap()比read()更快”
在我的结论中:
我对待MappedByteBuffer ==内存映射文件== mmap()
read()必须通过以下方式读取数据:磁盘文件 – >内核 – >应用程序,因此它具有上下文切换和缓冲区复制
他们都说mmap()比read()有更less的复制或系统调用,但是据我所知,第一次访问文件数据时也需要从磁盘文件中读取数据。 所以它第一次读取:虚拟地址 – >内存 – >页面错误 – >磁盘文件 – >内核 – >内存。 除了可以随意访问外,最后3步(磁盘文件 – >内核 – >内存)与read()完全相同,那么mmap()与read()的复制或系统调用的复制方式如何?
mmap()和交换文件之间的关系是什么,os会把内存中最less使用的文件数据交换(LRU)? 所以当你第二次访问这些数据时,OS从交换中检索它们而不是磁盘文件(不需要拷贝到内核缓冲区),这就是为什么mmap()的拷贝和系统调用较less的原因?
在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(),
这本身就是一个额外的步骤。