零拷贝
→ 返回 面试
「零拷贝」指减少数据在用户态与内核态之间无意义的 CPU 拷贝和上下文切换。详解 零拷贝。
传统 IO 问题(读文件发网络)
File.read(buf); // 磁盘 → 内核 → 用户
Socket.write(buf); // 用户 → 内核 → 网卡4 次拷贝(2 次 DMA + 2 次 CPU)+ 4 次上下文切换。
数据只是原样转发,却在用户空间「转了一圈」——②③ 两次 CPU 拷贝可省。
三种优化方案
| 方案 | CPU 拷贝 | 数据进用户空间 | Java API | 场景 |
|---|---|---|---|---|
| 传统 read/write | 2 次 | ✅ | InputStream | 需在应用层处理内容 |
| mmap + write | 1 次 | 映射访问 | MappedByteBuffer | 大文件随机读写 |
| sendfile | 1 次 | ❌ | FileChannel.transferTo() | 静态文件、Kafka |
| sendfile + SG-DMA | 0 次 | ❌ | transferTo(硬件支持) | Nginx、Kafka |
sendfile 流程
磁盘 ──DMA──► page cache ──CPU──► socket buffer ──DMA──► 网卡
(数据不进入 JVM 堆)
fileChannel.transferTo(0, size, socketChannel);限制:应用读不到字节内容,无法在同一链路里加密/改内容(需先处理再普通 write)。
谁在用
| 组件 | 用途 |
|---|---|
| Kafka | Broker 把日志 sendfile 给消费者 |
| Nginx | sendfile on; 静态资源 |
| Netty | FileRegion 封装 transferTo |
与 epoll 多路复用 配合:epoll 决定「何时发」;零拷贝决定「怎么发」——见 零拷贝 ≠ 非阻塞。
易混淆点
| 概念 | 说明 |
|---|---|
| 零拷贝 | 减少内核/用户态间 CPU 拷贝 |
| DirectByteBuffer | 堆外内存,减少 JVM 堆 ↔ 内核 拷贝;仍是另一维度 |
| Netty CompositeByteBuf | 逻辑合并缓冲区,避免合并时拷贝(用户态优化) |
常见面试题
Q:零拷贝是 0 次拷贝吗?
A:通常指 0 次 CPU 拷贝;DMA 拷贝仍存在。SG-DMA 下可连 CPU 拷贝也去掉。
Q:为什么 Kafka 快?和零拷贝关系?
A:顺序写 + Page Cache + sendfile 消费 + 批量 + 分区;零拷贝是其中之一。见 Kafka常见面试题。
Q:什么场景不能用零拷贝?
A:需要在应用层解析、加密、压缩、修改数据时,必须进用户缓冲区。
Q:mmap 和 sendfile 区别?
A:mmap 数据映射到用户空间可读;sendfile 数据不进用户空间,适合纯转发。