一文搞懂零拷贝(Zero-Copy)技术
这篇我用最直观的流程 + 极简图示,把传统 IO、mmap、sendfile、Netty 零拷贝全部讲透,面试、原理一步到位。
一、先看一张总览图(灵魂总结)
传统IO:
硬盘 → 内核读缓冲区 → 用户缓冲区 → Socket缓冲区 → 网卡
(4次拷贝 + 4次上下文切换)

零拷贝:
硬盘 → 内核缓冲区 → 网卡
(2次DMA拷贝 + 0次CPU拷贝 + 2次上下文切换)

一句话:
零拷贝 = 不让数据经过用户态,减少CPU拷贝与上下文切换。
二、传统 IO 到底慢在哪?(图解)
场景:读取文件,通过 Socket 发送到网络。
传统 IO 流程(4 拷贝 + 4 切换)
- 硬盘 → 内核读缓冲区(DMA 拷贝)
- 内核 → 用户缓冲区(CPU 拷贝)
- 用户 → Socket 发送缓冲区(CPU 拷贝)
- Socket 缓冲区 → 网卡(DMA 拷贝)
上下文切换(用户态 ↔ 内核态)
- read() → 切内核
- 数据拷贝 → 切回用户
- write() → 切内核
- 发送完成 → 切回用户
共 4 次上下文切换!
图示(文字版流程图)
[硬盘]
↓ DMA
[内核读缓冲区]
↓ CPU(用户态<->内核态切换)
[用户缓冲区]
↓ CPU(用户态<->内核态切换)
[Socket 缓冲区]
↓ DMA
[网卡]
问题:
数据根本不需要进用户态,却来回拷贝、反复切换,CPU 白白浪费。
三、零拷贝 1:mmap + write(图解)
原理
mmap:把内核缓冲区直接映射到用户空间。
用户进程和内核共享同一块内存,少一次 CPU 拷贝。
流程

- 硬盘 → 内核缓冲区(DMA)
- 用户空间直接访问内核缓冲区(无拷贝)
- 内核 → Socket 缓冲区(CPU)
- Socket → 网卡(DMA)
拷贝次数
- DMA 拷贝:2 次
- CPU 拷贝:1 次
- 上下文切换:4 次
图示
[硬盘]
↓ DMA
[内核缓冲区] ←─ mmap映射 ─→ [用户空间]
↓ CPU
[Socket 缓冲区]
↓ DMA
[网卡]
优点:比传统 IO 快。
缺点:仍有 1 次 CPU 拷贝 + 4 次切换。
四、零拷贝 2:sendfile(Linux 2.1)—— 真正零拷贝
Java NIO FileChannel.transferTo() 底层就是这个。
流程(真正零拷贝)

- 硬盘 → 内核缓冲区(DMA)
- 内核缓冲区 → Socket 缓冲区(CPU)
- Socket 缓冲区 → 网卡(DMA)
关键
数据全程不经过用户态!
拷贝次数
- DMA 拷贝:2 次
- CPU 拷贝:1 次
- 上下文切换:2 次
图示
[硬盘]
↓ DMA
[内核缓冲区]
↓(CPU)
[Socket 缓冲区]
↓ DMA
[网卡]
五、终极零拷贝:sendfile + SG-DMA(Linux 2.4)
最强版本:0 CPU 拷贝!
网卡支持 Scatter-Gather DMA,可以直接从内核缓冲区读取数据。
流程

- 硬盘 → 内核缓冲区(DMA)
- 网卡直接从内核缓冲区拉走数据(完全不拷贝)
图示
[硬盘]
↓ DMA
[内核缓冲区]
↓(无任何拷贝)
[网卡 直接读取]
最终结果
- CPU 拷贝:0 次
- 上下文切换:2 次
- 这就是 Kafka、Nginx、Netty 超快的原因。
六、一张表看懂所有拷贝对比(面试必背)
| 方式 | 拷贝次数 | CPU拷贝 | 上下文切换 | 特点 |
|---|---|---|---|---|
| 传统IO | 4次 | 2次 | 4次 | 最慢 |
| mmap | 3次 | 1次 | 4次 | 中等 |
| sendfile | 3次 | 1次 | 2次 | 零拷贝 |
| sendfile+SG-DMA | 2次 | 0次 | 2次 | 终极零拷贝 |
七、Java NIO 零拷贝代码(一行搞定)
// 文件直接发送到网络,不经过用户内存
fileChannel.transferTo(position, count, socketChannel);
底层:Linux → sendfile()
八、Netty 中的零拷贝(不止一种)
Netty 零拷贝是广义零拷贝,不只靠操作系统:
- ByteBuf:使用堆外内存,避免 JVM 堆拷贝
- CompositeByteBuf:合并多个缓冲区,无拷贝聚合
- FileRegion:包装 transferTo,操作系统级零拷贝
- wrap 方法:包装数组,不复制数据
九、面试高频 6 问(标准答案)
1. 零拷贝是 0 次拷贝吗?
不是。
是减少 CPU 拷贝 & 避免用户态内核态拷贝。
仍然有 2 次 DMA 拷贝。
2. 零拷贝底层靠什么?
Linux:sendfile()
Java:FileChannel.transferTo()
3. 传统 IO vs 零拷贝区别?
传统:4 拷贝 4 切换
零拷贝:2 拷贝 2 切换,0 CPU 拷贝
4. 为什么 Kafka 这么快?
大量使用 零拷贝 + 页缓存。
5. mmap 和 sendfile 区别?
mmap:共享内存,减少 1 次拷贝
sendfile:完全不进用户态,真正零拷贝
6. 零拷贝不能做什么?
不能加密、压缩、修改数据,因为数据不进用户态。
十、终极一句话总结
零拷贝就是让数据从硬盘直接到网卡,不经过用户态,减少2次CPU拷贝和2次上下文切换,是高并发大文件传输的性能核武器。

浙公网安备 33010602011771号