文件的写操作同样如此,进行写操作时,将数据先写到Page Cache的缓冲区中,后续由操作系统将数据刷回到磁盘中 。
缓存I/O的优缺点
优点:减少磁盘I/O次数,提升读写性能 。
缺点:数据需要在内核空间和用户空间来回拷贝 。
DirectByteBuffer使用缓存I/O读取数据时,数据会经过两次拷贝,经过两次拷贝是从系统调用开始讲起,在JAVA中由于涉及到JVM堆内和堆外内存,如果使用java.io
下的类进行文件读写实际上还会再多一次拷贝(详细可参考【JAVA】普通IO数据拷贝次数的问题探讨):
- 底层发起JNI调用,创建堆外缓冲区;
- JNI中发起read系统调用,此时需要由用户空间切换到内核空间;
- 进入到内核空间,DMA读取文件数据到内核缓冲区(DMA拷贝);
- 将内核缓冲区的数据拷贝到用户缓冲区(CPU拷贝),切换回用户空间;
- 将堆外缓冲区的数据拷贝到JVM堆内缓冲区中(CPU拷贝);

文章插图
在Java的NIO中,提供了DirectByteBuffer,可以直接分配堆外内存,减少了一次从堆外内存到堆内内存的复制(CPU复制):

文章插图
直接I/O缓存I/O经过了Page Cache,读取过程中需要将数据从Page Cache的缓冲区中拷贝到用户空间的缓存区,那么有没有一种方式可以省去这个拷贝的过程?
答案是有的,那就是直接I/O,应用程序直接访问磁盘数据,绕过了Page Cache,省去了从内核缓冲区拷贝到用户缓冲区的过程:

文章插图
目前JAVA并没有原生的直接/O操作方式,不过公众号博主Kirito提供了在JAVA中进行直接I/O操作的方法,具体参见【Kirito的技术分享】Java 文件 IO 操作之 DirectIO 。
内存映射内存映射就是将虚拟空间地址映射到物理空间地址,每个进程维护了一张页表,记录虚拟地址和物理地址之间的映射关系,当进程访问的虚拟地址在页表中无法查到映射关系时,系统产生缺页异常,进入内核空间为虚拟地址分配物理内存,并更新页表,记录映射关系 。
文件映射
内存映射除了映射虚拟空间地址和物理空间地址,还包括将磁盘的文件内容映射到虚拟地址空间,称为文件映射,此时可以通过访问内存来访问文件里面的数据。
mmap系统调用可以将文件映射到虚拟内存空间 。文件映射的流程如下:
- 进行mmap系统调用,将文件和虚拟地址空间建立映射,注意此时还没有分配物理内存空间,只是在逻辑上建立了虚拟地址和文件之间的映射关系,物理内存只有真正使用的时候才会分配 。
- 应用程序访问用户空间虚拟内存中的某个地址,发现无法在页表中查到数据,产生缺页异常,此时进入内核空间
- 因为不能直接使用物理地址,所以需要使用内核的虚拟地址临时建立与物理内存的映射关系,将文件内容读取到物理内存中,待数据读取完毕之后取消临时映射即可 。
- 缺页异常处理完毕,物理内存中已经加载了文件的数据,此时用户空间就可以通过虚拟地址直接访问物理内存中映射的文件数据 。

文章插图
从文件映射的流程中可以看出它与缓存I/O相比,少了从内核缓冲区将数据拷贝到用户缓冲区的步骤,减少了一次拷贝 。
经验总结扩展阅读
- 2023年2月9日是安装门框吉日吗 2023年2月9日是安装门框的黄道吉日吗
- 猪肉沫一块是什么梗
- 2023年2月9日是堵蚂蚁洞吉日吗 2023年2月9日堵蚂蚁洞行吗
- 我去厨房给你拿钱是什么意思梗
- 百货批发部从哪里进货渠道
- 猪屎入鸡窝是什么梗
- 原神沙海迷踪火把机关怎么解密
- 秋分和立秋哪个是秋天的开始
- 2023年2月9日航行好不好 2023年农历正月十九航行吉日
- 支付宝蚂蚁庄园10月12日答案是什么