NIO 与 BIO 的对比

IO模型

用户线程读取数据,当调用channel.read 或 stream.read后,会由用户空间切换至操作系统的内核空间来真正读取数据,而读取数据又分为两个阶段,分别为:

  1. 等待数据阶段
  2. 复制数据阶段

NIO 与 BIO 的对比

阻塞IO(同步)

如上图,用户线程发起了一个read调用,真正读取数据,要切换到内核空间,由操作系统去读数据。当网络中没有数据发送过来时,read调用就会阻塞,表现为用户线程在等待。等数据从网络传输过来后,操作系统将数据从网卡复制到内存,等数据真正复制完成,那么就从内核空间切换回用户空间,此时read方法调用完成。

非阻塞IO(同步)

如上图,用户线程发起read调用,切换到内核空间。如果发现数据还不存在,会直接返回。但是,如果发现正在复制数据,还是会等待数据复制完成后返回。因此,非阻塞IO发生在等待数据阶段。复制数据阶段,IO还是阻塞的。

多路复用(同步)

用户线程,发起 selector的select()方法调用,此时,线程阻塞。检测有没有事件发生,如果有事件发生,内核告诉用户线程,select()方法不再阻塞。接下来就可以根据发生的事件类型,如发生读事件,那么用户线程就会发起read调用,去内核读取数据。

异步IO
  1. 同步:只有一个线程做,从发起系统调用,到结果返回,都由一个线程来做。中间,可能会阻塞,线程做不了其他事情。
  2. 异步:不是一个线程完成,至少有两个线程。如一个线程发起系统调用后,就不管了。交给操作系统去执行,有结果返回时,另外一个线程,将结果返回。返回的结果,会触发原来线程的回调函数,让第一个线程回来继续执行。

异步IO是非阻塞的,不存在阻塞的情况。

零拷贝

传统IO问题

传统IO将一个文件通过socket写出

File f = new File("mydata/data.txt");
RandomAccessFile file = new RandomAccessFile(file, "r");

byte[] buf = new byte[(int)f.length()];
file.read(buf); //把文件读到数组中

Socket socket = ...;
socket.getOutputStream().write(buf);

NIO 与 BIO 的对比

  1. java本身并不具备IO读写能力,因此read方法调用后,要从java程序用户态,切换至内核态(第一次:用户态到内核态切换),去操作系统的内核读数据。将数据读入内核缓冲区中(第一次数据拷贝)。这个期间,用户线程会阻塞,操作系统使用DMA(Direct Memory Access)来实现数据读取,且不使用CPU。
  2. 从内核态切换到用户态(第二次:内核态到用户态),将内核缓冲区的数据,复制到用户缓冲区(第二次数据拷贝)。这期间会用到CPU无法使用DMA。
  3. 调用write方法后,将用户缓冲区的数据拷贝到socket缓冲区(第三次数据拷贝)。
  4. 接下来向网卡写数据,这项能力java又不具备,因此,要从用户态切换到内核态(第三次:用户态到内核态切换),使用DMA将socket缓冲区的数据拷贝到网卡(第四次数据拷贝),不会使用CPU。

数据拷贝进行了4次,用户态与内核态切换进行了3次。

NIO优化后
  1. 通过DirectByteBuf
    • ByteBuffer.allocate(10): HeapByteBuffer使用的还是Java内存
    • ByteBuffer.allocateDirect(10):DirectByteBuffer 使用的是操作系统内存(特点是:操作系统可以访问,用户也可以访问)

NIO 与 BIO 的对比
Java 使用了 DirectByteBuf 将对外内存映射到JVM内存来直接访问。读写过程中,减少了一次数据拷贝。但是内核态到用户态切换没有减少。

  1. linux 2.1 优化

    Java中,channel调用transferTo和TransferFrom后,数据拷贝如图:
    NIO 与 BIO 的对比
    数据拷贝只在内核态完成,发生了三次数据拷贝。

  2. linux 2.4
    Java中,channel调用transferTo和TransferFrom后,数据拷贝如图:

NIO 与 BIO 的对比
数据拷贝只发生了两次,而内核缓冲区到socket缓冲区,只是拷贝了数据的length,offset等内容,速度极快。

真正的零拷贝,其实不是没有数据拷贝。只是没有将数据拷贝到JVM内存。

上一篇:[Netty学习笔记]四、NIO核心组件Selector


下一篇:Java网络编程(8)NIO - Selector详解