Netty中的零拷贝是怎么实现的?
Netty是Java语言中一个高性能的网络通信框架,零拷贝又是这个框架的特色之一,它是如何实现的呢?
在计算机中完成一次数据传输,一般需要经过两个阶段。第一步,操作系统把数据从本地硬盘或网卡拷贝到内核空间的内存;第二步,应用程序再把数据从系统内核空间的内存拷贝到用户空间的内存;接下来才是应用程序中的数据处理工作。
先来看几个名词。
DMA(Direct Memory Access)直接存储器访问,将数据从一个地址空间复制到另一个地址空间。当CPU初始化这个传输动作后,传输动作本身是由DMA控制器(DMAC)来完成的。也就是说在数据传输期间,系统可以并行执行其他任务。CPU拷贝,是由CPU直接处理的数据的传送,数据拷贝时一直占用CPU资源。
从上图中可以看出,传统的IO读写流程,包括4次用户态和内核态的切换,4次上下文切换,4次的数据拷贝,2次CPU拷贝,2次DMA拷贝。
一、什么是零拷贝?
拷贝,是指数据从一个存储区域复制到另一个存储区域。 零,表示次数为0,复制的次数为0,也就是数据不需要从一个存储区域复制到另一个存储区域。
二、为什么需要零拷贝?
零拷贝,就是指从系统内核空间的内存到用户空间的内存,不需要采用传统方式的数据复制。而是将系统内核空间的内存和用户空间的内存实现关联映射(mmap内存映射机制),从而省去了数据传输过程中的复制。
mmap(memory map)内存映射机制,简单来说就是将文件/设备映射到内存中,进程可以通过读写内存的方式,实现对mmap文件的操作。零拷贝并不是完全没有拷贝,而是减少了数据拷贝的次数。
三、零拷贝在Netty中的三种实现。
1.使用堆外内存,也叫直接内存(Direct Memory)。netty的接收和发生都是使用Direct buffer,对应系统底层的mmap机制,直接使用堆外内存进行Socket读写,不需要进行字节缓冲区的二次拷贝。
2.提供了组合buffer对象 (CompositeByteBuf),可以聚合多个ByteBuffer对象,用户只需要像操作一个ByteBuffer一样操作组合ByteBuffer,避免了传统通过内存拷贝的方式将几个buffer合并成一个大buffer,不需要内存拷贝。
3.文件传输采用TransferTo方法,它可以直接将文件缓冲区的数据发送到目标channel,避免了传统通过循环write方式导致的内存拷贝问题。
最后总结
通过整理可以发现,netty的零拷贝并不是完全不拷贝,而是减少了CPU拷贝,也就是数据从系统内核空间的内存到用户空间内存的拷贝。DMA拷贝还是存在的,毕竟它是操作系统所做的事情,不属于应用程序的操作范围。在netty中,目前有三种方式实现的零拷贝。第一种使用堆外内存。第二种,CompositeByteBuf组合buffer对象。第三种,文件传输采用TransferTo方法。