Netty的零拷贝体现在三个方面:
- buffer层面
对于ByteBuf,Netty提供了多种实现:
a. Heap ByteBuf:直接在堆内存分配
b. Direct ByteBuf:直接在内存区域分配而不是堆内存(零拷贝)
c. CompositeByteBuf:组合Buffer(零拷贝)
1.1 ByteBuffer:Netty的接收和发送ByteBuffer采用DIRECT BUFFERS,使用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝。如果使用传统的堆内存(HEAP BUFFERS)进行Socket读写,JVM会将堆内存Buffer拷贝一份到直接内存中,然后才写入Socket中。相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。
DIRECT BUFFERS直接在内存区域分配空间,而不是在堆内存中分配。如果使用传统的堆内存分配,当我们需要将数据通过socket发送的时候,就需要从堆内存拷贝到直接内存,然后再由直接内存拷贝到网卡接口层。
Netty提供的直接Buffer,直接将数据分配到内存空间,从而避免了数据的拷贝,实现了零拷贝
1.2 Netty提供了组合Buffer对象,即Composite Buffers,可以聚合多个ByteBuffer对象,用户可以像操作一个Buffer那样方便的对组合Buffer进行操作,避免了传统通过内存拷贝的方式将几个小Buffer合并成一个大的Buffer。
传统的ByteBuffer,如果需要将两个ByteBuffer中的数据组合到一起,我们需要首先创建一个size=size1+size2大小的新的数组,然后将两个数组中的数据拷贝到新的数组中。但是使用Netty提供的组合ByteBuf,就可以避免这样的操作,因为CompositeByteBuf并没有真正将多个Buffer组合起来,而是保存了它们的引用,从而避免了数据的拷贝,实现了零拷贝
2.操作系统层面
Netty的文件传输采用了transferTo方法,它可以直接将文件缓冲区的数据发送到目标Channel,避免了传统通过循环write方式导致的内存拷贝问题
传统意义的网络传输主要包括:
这种方式需要四次数据拷贝和四次上下文切换:
a. 数据从磁盘读取到内核的read buffer
b. 数据从内核缓冲区拷贝到用户缓冲区
c. 数据从用户缓冲区拷贝到内核的socket buffer
d. 数据从内核的socket buffer拷贝到网卡接口的缓冲区
明显上面的第二步和第三步是没有必要的,通过java的FileChannel.transferTo方法,可以避免上面两次多余的拷贝(当然这需要底层操作系统支持).netty中采用nio的零拷贝后:
a. 调用transferTo,数据从文件由DMA引擎拷贝到内核read buffer
b. 接着DMA从内核read buffer将数据拷贝到网卡接口buffer
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。