1.什么是零拷贝

传统 I/O 一次网络发送文件,数据往往要经过多次拷贝:

磁盘 → 内核缓冲区 → 用户缓冲区 → Socket 缓冲区 → 网卡
      (DMA)       (CPU拷贝)    (CPU拷贝)

零拷贝(Zero-Copy) 的目标不是“完全不拷贝”,而是减少 CPU 在用户态与内核态之间的来回拷贝次数,让数据尽量在内核里直接流转,降低 CPU 开销和内存带宽消耗。

2. Java 中的几种实现

2.1 FileChannel.transferTo() 

利用操作系统底层 sendfile(Linux)等机制,文件数据从磁盘经内核直接送到 Socket,跳过用户空间。

try (FileChannel fileChannel = FileInputStream.open(path).getChannel();
     SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress(host,port))) {

    // 一次调用,内核完成大部分搬运
    fileChannel.transferTo(0, fileChannel.size(), socketChannel);
}

适用:静态文件服务器、大文件下载、消息中间件转发文件。

2.2 FileChannel.transferFrom() 

从 Socket/其他 Channel 直接写入文件 Channel,原理类似。

fileChannel.transferFrom(socketChannel, 0, Long.MAX_VALUE);

2.3 MappedByteBuffer — 内存映射

通过 FileChannel.map() 把文件映射到堆外内存,读写像操作数组一样,减少 read/write 系统调用。

try (RandomAccessFile raf = new RandomAccessFile(path, "r");
      FileChannel channel = raf.getChannel()) {

    MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());

    // 直接读 buffer,无需额外 byte[] 中转
    while (buffer.hasRemaining()) {
        socketChannel.write(buffer); // 仍可能有一次拷贝到 Socket
    }
}

优势:大文件随机读、索引文件访问快。
注意:映射大文件占虚拟内存;修改需 force() 刷盘。

2.4 DirectByteBuffer — 堆外缓冲区

NIO 的 Direct Buffer 在内核可直接 DMA 访问,避免 JVM 堆 → 内核的额外拷贝。

ByteBuffer buf = ByteBuffer.allocateDirect(8192);

channel.read(buf); // 读到堆外

buf.flip();

socketChannel.write(buf);

适用:高频网络 I/O;代价是分配/回收比堆内 Buffer 慢。

2.5 Netty 的 FileRegion

Netty 对 transferTo 做了封装,配合 EventLoop 异步发送:

// Netty 示例
DefaultFileRegion region = new DefaultFileRegion(file, 0, file.length());
ctx.writeAndFlush(region);

RocketMQ、Kafka 等中间件底层也大量使用类似机制。

3.总结

方式 用户态拷贝 典型场景

传统 byte[] 读写

多次

小数据、逻辑简单

transferTo/From

极少

文件发送/落盘

MappedByteBuffer

大文件读、索引

DirectByteBuffer

网络 I/O 缓冲

Netty FileRegion

极少

高性能网络框架


使用注意

  • 并非所有场景都更快:小文件、需要业务解析的数据,零拷贝收益有限,反而增加复杂度。
  • transferTo 有平台差异:不同 OS/JDK 对 sendfile 支持程度不同,大文件可能分多次传输。
  • MappedByteBuffer 释放:依赖 GC 清理 Direct Memory,大映射要控制生命周期。
  • Java 9+ 提供了 InputStream.transferTo(OutputStream),底层也可能走优化路径,但网络场景仍优先 FileChannel

更多推荐