转载

netty中的零拷贝

Netty的零拷贝体现在三个方面:

  1. 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方式导致的内存拷贝问题

传统意义的网络传输主要包括:

netty中的零拷贝

这种方式需要四次数据拷贝和四次上下文切换:

a. 数据从磁盘读取到内核的read buffer

b. 数据从内核缓冲区拷贝到用户缓冲区

c. 数据从用户缓冲区拷贝到内核的socket buffer

d. 数据从内核的socket buffer拷贝到网卡接口的缓冲区

明显上面的第二步和第三步是没有必要的,通过java的FileChannel.transferTo方法,可以避免上面两次多余的拷贝(当然这需要底层操作系统支持).netty中采用nio的零拷贝后:

netty中的零拷贝

a. 调用transferTo,数据从文件由DMA引擎拷贝到内核read buffer

b. 接着DMA从内核read buffer将数据拷贝到网卡接口buffer

原文  https://segmentfault.com/a/1190000021490174
正文到此结束
Loading...