Netty-03-零拷贝

Netty-03-零拷贝

前言

在Java程序中,常用的零拷贝有 mmap(内存映射)和 sendFile。

零拷贝不仅仅带来更少的数据复制,还能减少线程的上下文切换,减少CPU缓存伪共享以及无CPU校验和计算。

1. 传统的IO读写

1
2
3
4
5
6
7
8
File file = new File("test.txt");
RandomAccessFile raf = new RandomAccessFile(file, "rw");

byte[] arr = new byte[(int) file.length()];
raf.read(arr);

Socket socket = new ServerSocket(8080).accept();
socket.getOutputStream().write(arr);

mark

  • 三次内核态用户态切换
  • 四次拷贝
    • DMA
    • kernel buffer - > user buffer
    • user buffer -> socket buffer
    • DMA

2. mmap 优化

mmap优化的IO读写:

  • mmap通过内存映射,将文件映射到内核缓冲区,同时,用户空间可以共享内存空间的数据。这样,在进行网络传输时,就可以减少内核空间到用户空间的拷贝次数。
  • 需要进行3次上下文切换,3次数据拷贝。
  • 适合小数据量的读写。

mark

3. sendFile 优化

  • Linux2.1 版本提供了 sendFile 函数,其基本原理如下:数据根本不经过用户态,直接从内核缓冲区进入到SocketBuffer,同时,由于和用户态完全无关,就减少了一次上下文切换。
  • 需要3次上下文切换和最少2次数据拷贝。
  • 适合大文件的传输。

mark

  • 而 Linux 在 2.4 版本中,做了一些修改,避免了从内核缓冲区拷贝到 Socket Buffer 的操作,直接拷贝到协议栈,从而再一次减少了数据拷贝。

mark

注:这里其实有一次CPU拷贝,kernel buffer -> socket buffer。但是,拷贝的信息很少,只拷贝了数据的长度、偏移量等关键信息,消耗低,可以忽略不计。

4. NIO中的零拷贝(transferTo)

  • 客户端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class NIOzerocopy {
public static void main(String[] args) throws IOException {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("localhost",7001));

// 得到一个文件的channel
FileChannel channel = new FileInputStream("a.zip").getChannel();

// 准备发送
long startTime = System.currentTimeMillis();

// 在linux中一个transfer函数就可以完成传输
// 在windows中调用一次transferTo 只能发送8M,就需要把数据分段传输
// transferTo 底层使用到了零拷贝
long transferCount = channel.transferTo(0, channel.size(), socketChannel);

// 结束发送
System.out.println("发送的总的字节数:" + transferCount + " 耗时:" + (System.currentTimeMillis() - startTime));
channel.close();
}
}
打赏
  • 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!
  • © 2019-2022 Zhuuu
  • PV: UV:

请我喝杯咖啡吧~

支付宝
微信