1、NIO非阻塞
NIO(Non-blocking I/O),是一种同步非阻塞的I/O模型,也是I/O多路复用的基础,已经被越来越多地应用到大型应用服务器,称为处理解决高并发与大量连接、I/O处理的有效方式。
与NIO对比,以前使用最多的是BIO(blocking IO),也就是阻塞式IO,这种io模式在活动连接数不是特别高的时候(小于1000),还是非常不错的,当io阻塞系统时可以利用多线程,最大限度地使用CPU资源。
Linux提供的零拷贝技术Java并不是全部支持,目前只支持两种(内存映射mmap/sendfile),NIO提供的sendfile, FileChannel.transferTo()方法直接将当前通道内容传输到另外一个通道,没有涉及到Buffer的任何操作,NIO中的Buffer是JVM堆或者堆外内存,但不论如何他们都是操作系统内核空间的内存。transferTo()的实现方式就是通过系统调用sendfile()(是通过Linux中的系统调用)。值得注意的是Java NIO提供的FileChannel.transferTo 和 transferFrom 并不保证一定能使用零copy。实际上是否能使用零拷贝与操作系统有关,如果操作系统提供sendfile 这样的零拷贝系统函数,那么这两个方法就会借助这样的系统调用充分利用零拷贝的优势,否则并不能通过这两个方法本身实现零拷贝的。
2、Linux支持的常见零拷贝
DMA(Direct Memory Access,直接内存存取)是所有现代电脑的重要特色,它允许不同速度的硬件装置来沟通,而不需要依赖于CPU的大量中断负载。
(1)mmap内存映射
DMA加载磁盘数据到kernel buffer之后,应用程序缓冲区(application buffers)和内核缓冲区(kernel buffer)进行映射,数据在应用缓冲区和内核缓冲区的改变就能省略
mmap 内存映射将会经历3次copy,1次cpu拷贝,2次DMA copy以及4次上下文切换
(2)sendfile
linux 2.1 支持的sendfile.当调用sendfile()时,DMA将磁盘数据复制到kernel buffer, 然后将内核中的kernel buffer 直接拷贝到socket buffer;一旦数据全部拷贝到socket buffer,
sendfile()系统调用将会return,代表数据的转化的完成。socket buffer 里的数据剧可以在网络里面进行网络传输了
sendfile 会经历:3次拷贝,1次cpu拷贝 2次DMA 拷贝,以及2次上下文切换。
我们用传统的IO以及NIO 零copy分别复制不同代销的文件,零拷贝随着文件的增大省出的时间还是非常可观的。
/**
* @Author: chenxiangweifeng
* @Date: 2020/12/20 16:40
* java nio java non-balocking java非阻塞式IO操作
*/
public class NioDemo {
@Test
public void zeroCopy() throws Exception{
long start = System.currentTimeMillis();
File src = new File("D://Linux-iso/ubuntu-16.04.3-desktop-amd64.iso");
File dst = new File("D://dst-zero.iso");
FileChannel in = FileChannel.open(src.toPath(), StandardOpenOption.READ);
FileChannel out = FileChannel.open(dst.toPath(), StandardOpenOption.CREATE, StandardOpenOption.WRITE);
in.transferTo(0,in.size(),out);
long end = System.currentTimeMillis();
System.out.println("复制耗时毫秒数:"+(end-start));
in.close();
out.close();
}
@Test
public void directCopy() throws Exception{
long start = System.currentTimeMillis();
File src = new File("D://Linux-iso/ubuntu-16.04.3-desktop-amd64.iso");
File dst = new File("D://dst.iso");
BufferedInputStream in = new BufferedInputStream(new FileInputStream(src));
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(dst));
byte[] buffer = new byte[1024];
int len = 0;
int count = 0;
while ((len = in.read(buffer)) != -1 ){
count++;
out.write(buffer);
}
long end = System.currentTimeMillis();
System.out.println("循环次数:"+count);
System.out.println("复制耗时毫秒数:"+(end-start));
in.close();
out.close();
}
结果我们复制一个112mb的文件
文件大小 | 传统IO (ms) | 零copy (ms) |
112MB | 1198 | 773 |
1GB | 37080 | 10957 |
可以看出、使用零copy技术在文件传输大文件上速度还是非常快!
参考文档:Java中的零拷贝 https://www.jianshu.com/p/2fd2f03b4cc3