学习 NIO 之 零拷贝
学习 NIO 之 零拷贝
一、什么是零拷贝
引用维基百科的原话
"Zero-copy" describes computer operations in which the CPU does not perform the task of copying data from one memory area to another. This is frequently used to save CPU cycles and memory bandwidth when transmitting a file over a network.[1]
"零拷贝"描述了CPU没有执行从一块内存区域复制数据到另一块内存区域的操作. 这经常用在通过网络传输文件时节省CPU周期和内存带宽.
二 、内核空间与用户空间
引用维基百科的原话
A modern computer operating system usually segregates virtual memory into kernel space and user space.[a] Primarily, this separation serves to provide memory protectionand hardware protection from malicious or errant software behaviour.
Kernel space is strictly reserved for running a privileged operating system kernel, kernel extensions, and most device drivers. In contrast, user space is the memory area where application software and some drivers execute.
现代计算机操作系统通常将虚拟内存分离为内核空间和用户空间。这种分离主要用于提供内存保护和硬件保护,以防止恶意或错误的软件行为。 内核空间严格保留用于运行特权操作系统内核,内核扩展和大多数设备驱动程序。相反,用户空间是应用软件和一些驱动程序执行的存储区域。
三、理解
由于存在用户空间和内核空间, 一般情况下读取文件需要将文件从硬盘读取到内核空间, 再将数据复制到用户空间, 写文件时, 需要将文件从用户空间复制到内核空间,再将数据从内核空间写入硬盘. 在某些情况下, 用户空间只是临时的中介内存, 复制多了两次, 造成了不必要的性能浪费. 其中,用户空间和内核空间的上下文切换也是需要消耗性能的.
为了减少不必要的数据复制, 于是产生了"零拷贝". 数据从硬件读取到内核空间之后, 不再经过用户空间, 直接在内核空间中操作数据或移动数据, 最后将数据输出到其他设备中.
一般情况下, 有两种零拷贝
第一种, 需要操作数据的, 仅仅只是将数据从一个设备移动到另一设备, 数据在内核空间中移动(在Linux中使用sendfile系统调用), 没有经过用户空间, 用户无法操作数据.
第二种, 需要操作数据的, 使用内存映射文件(mmap)方法, 将文件按照一定大小块映射为内存区域, 当程序访问这个内存区域时, 将直接操作这个文件数据,省去了数据从内核空间向用户空间复制的损耗.
详细的调用过程可以查看此博文
这两种方式, 分别对应了java nio中的FileChannel.transferXXX和FileChannel.map
四、FileChannel.transferTo和FileChannel.transferFrom使用例子
/** * Nio中的零拷贝方式, * FileChannel.transferTo, FileChannel.transferFrom * @author TimFruit * @date 2019/7/3 13:21 */ public class ZeroCopyTests { public static void main(String[] args) throws IOException { //filechannel.transferXXXx File file=new File("test.txt"); FileChannel fromChannel=new FileInputStream(file).getChannel(); File newFile=new File("test_new.txt"); FileChannel newChannel=new FileOutputStream(newFile).getChannel(); // 零拷贝,数据在内核空间中移动, 并不会从内核空间复制数据到用户缓存中 int position=0; int count=5; long result; for(;;){ result=fromChannel.transferTo(position, count, newChannel); position+=count; if(result==0){ break; } } // 还有一个transferFrom方法 } }
五、FileChannel.map 使用例子
/** * Nio中的零拷贝方式 * FileChannel.map 将文件按照一定大小块映射为内存区域,当程序访问这个内存区域的时候,将直接操作这个文件数据 * 这种方式省却了数据从内核空间箱用户空间复制的损耗,这种方式适合对大文件的只读性操作,如大文件的MD5操作 * @author TimFruit * @date 2019/7/3 13:36 */ public class ZeroCopyTests2 { public static void main(String[] args) throws IOException { File file=new File("test_map.txt"); RandomAccessFile ras=new RandomAccessFile(file, "r"); FileChannel channel=ras.getChannel(); // filechannel直接映射内核空间,在内核空间中操作数据, 省去了从内核空间拷贝到用户空间的操作 // 适合对大文件的只读操作 MappedByteBuffer mbb=channel.map(FileChannel.MapMode.READ_ONLY, 0,10); byte b=mbb.get(1); System.out.println(Character.toChars(b)); //b } }
学习资料:
<深入分析 java web 技术内幕> (修订版)