零拷贝 Zero-copy

2018/9/28

首先Zero-copy 的定义:

"Zero-copy" describes computer operations in which the CPU does not perform the task of copying data from one memory area to another. This is frequently used to save CPU cycles and memory bandwidth when transmitting a file over a network.

OS 层面的 Zero-copy

在 OS 层面上的 Zero-copy 通常指避免在 用户态(User-space) 与 内核态(Kernel-space) 之间来回拷贝数据.

Linux 提供的 mmap 系统调用, 它可以将一段用户空间内存映射到内核空间,当映射成功后,用户对这段内存区域的修改可以直接反映到内核空间; 同样地, 内核空间对这段区域的修改也直接反映用户空间. 正因为有这样的映射关系, 我们就不需要在 用户态(User-space) 与 内核态(Kernel-space) 之间拷贝数据, 提高了数据传输的效率.

mmap的文件映射:可以把文件的一部分或全部映射到内存中,通过MappedBuffer对内存进行操作,操作结果会由操作系统负责flush到文件中。

代码示例:

RandomAccessFile raf = new RandomAccessFile (File, "rw");

FileChannel channel = raf.getChannel();

MappedByteBuffer buff = channel.map(FileChannel.MapMode.READ_WRITE,startAddr,SIZE);

buf.put((byte)255);

buf.write(byte[] data)

Netty的 Zero-copy

Netty的 Zero-copy 完全是在用户态(Java 层面)的, 它的 Zero-copy 的更多的是偏向于优化数据操作这样的概念.

Netty 的 Zero-copy 体现在如下几个个方面:

1.Netty 提供了 CompositeByteBuf 类, 它可以将多个 ByteBuf 合并为一个逻辑上的 ByteBuf, 避免了各个 ByteBuf 之间的拷贝.

addComponents(boolean increaseWriterIndex, ByteBuf... buffers)


2.通过 wrap 操作, 我们可以将 byte[] 数组、ByteBuf、ByteBuffer等包装成一个 Netty ByteBuf 对象, 进而避免了拷贝操作.

ByteBuf byteBuf = Unpooled.wrappedBuffer(bytes);

3.ByteBuf 支持 slice 操作, 因此可以将 ByteBuf 分解为多个共享同一个存储区域的 ByteBuf, 避免了内存的拷贝.

slice 操作可以将一个 ByteBuf 切片 为多个共享一个存储区域的 ByteBuf 对象.

ByteBuf header = byteBuf.slice(0, 5);

ByteBuf body = byteBuf.slice(5, 10);

4.通过 FileRegion 包装的FileChannel.tranferTo 实现文件传输, 可以直接将文件缓冲区的数据发送到目标 Channel, 避免了传统通过循环 write 方式导致的内存拷贝问题.

RandomAccessFile srcFile = new RandomAccessFile(srcFileName, "r");

FileChannel srcFileChannel = srcFile.getChannel();

RandomAccessFile destFile = new RandomAccessFile(destFileName, "rw");

FileChannel destFileChannel = destFile.getChannel();

long position = 0;

long count = srcFileChannel.size();

srcFileChannel.transferTo(position, count, destFileChannel);

使用了 FileChannel 后, 我们就可以直接将源文件的内容直接拷贝(transferTo) 到目的文件中, 而不需要额外借助一个临时 buffer, 避免了不必要的内存操作.