首先将它与传统的I/O read和write操作作对比,看看有什么不同,首先需要理解一下用户态和内存态的概念
用户态(User Mode)和内核态(Kernel Mode),也可以叫用户空间和内核
用户态:受限的访问内存,并且不允许访问硬件设备。
内核态:本质上是一个软件,可以控制计算机的硬件资源(如网卡,硬盘),可以访问内存所有数据。
用户程序都是运行在用户态中的,比如JVM,就是用户程序,所以它运行在用户态中。
用户态是不能直接访问硬件设备的,如果需要一次I/O操作,那就必须利用系统调用机制切换到内核态(用户态与内核态之间的转换称为上下文切换),进行硬盘读写。
比如说一次传统网络I/O:
第一步,从用户态切换到内核态,将用户缓冲区的数据拷贝到内核缓冲区,执行send操作。
第二步,数据发送由底层的操作系统进行,此时从内核态切换到用户态,将内核缓存区的数据拷贝到网卡的缓冲区
总结:也就是一次普通的网络I/O,至少经过两次上下文切换,和两次内存拷贝。
什么是零拷贝?
当需要传输的数据远大于内核缓冲区的大小时,内核缓冲区就成为I/O的性能瓶颈。零拷贝就是杜绝了内核缓冲区与用户缓冲区的的数据拷贝。
所以零拷贝适合大数据量的传输。
拿传统的网络I/O做对比,零拷贝I/O是怎样的一个过程:
用户程序执行transferTo(),将用户缓冲区待发送的数据拷贝到网卡缓冲区。
很简单,一步完成,中间少了用户态到内存态的拷贝。
Netty中零拷贝如何实现
Netty的中零拷贝与上述零拷贝是不一样的,它并不是系统层面上的零拷贝,只是相对于ByteBuf而言的。
Netty中的零拷贝:
1.CompositeByteBuf,将多个ByteBuf合并为一个逻辑上的ByteBuf,避免了各个ByteBuf之间的拷贝。
使用方式:
CompositeByteBuf compositeByteBuf = Unpooled.compositeBuffer();
compositeByteBuf.addComponents(true, ByteBuf1, ByteBuf1);
注意:addComponents第一个参数必须为true,那么writeIndex才不为0,才能从compositeByteBuf中读到数据。
2.wrapedBuffer()方法,将byte[]数组包装成ByteBuf对象。
byte[] bytes = data.getBytes();
ByteBuf byteBuf = Unpooled.wrappedBuffer(bytes);
Unpooled.wrappedBuffer(bytes)就是进行了byte[]数组的包装工作,过程中不存在内存拷贝。
即包装出来的ByteBuf和byte[]数组指向了同一个存储空间。因为值引用,所以bytes修改也会影响byteBuf 的值。
3.ByteBuf的分割,slice()方法。将一个ByteBuf对象切分成多个ByteBuf对象。
ByteBuf directByteBuf = ByteBufAllocator.DEFAULT.directBuffer(1024);
ByteBuf header = directByteBuf.slice(0,50);
ByteBuf body = directByteBuf.slice(51,1024);
header和body两个ByteBuf对象实际上还是指向directByteBuf的存储空间。
转自https://www.cnblogs.com/superfj/p/9242968.html