[转帖]IO、NIO、BIO 傻傻分不清吗,让我对象告诉你~~

io,nio,bio,傻傻,分不清,对象,告诉 · 浏览次数 : 0

小编点评

**Stream 与 Channelstream 不会自动缓冲数据** Stream 和 Channelstream 都是 Java 流 API,但它们在处理数据时有区别。 **Stream** * 是单向流动的。 * 使用用户态线程获取数据。 * 与阻塞 API 和非阻塞 API 混合使用。 * 网络 channel 可配合 selector 实现多路复用二者均为全双工。 **Channelstream** * 是双向流动的。 * 使用操作系统内核态线程获取数据。 * 与阻塞 API 和非阻塞 API 同义。 * 网络 channel 不可配合 selector 实现多路复用。 **主要区别** | 特性 | Stream | Channelstream | |---|---|---| | 流方向 | 单向 | 双向 | | 获取结果 | 用户态 |内核态 | | 与阻塞 API 的支持 | 阻塞和非阻塞 | 同义 | | 网络 channel 支持 | 同义 | 非同步 | | 多路复用 | 支持 | 不可支持 |

正文

https://my.oschina.net/jiagoushi/blog/5783304

 

 

1、Stream 与 Channel

  • stream 不会自动缓冲数据,channel 会利用系统提供的发送缓冲区、接收缓冲区(更为底层)
  • stream 仅支持阻塞 API,channel 同时支持阻塞、非阻塞 API,网络 channel 可配合 selector 实现多路复用
  • 二者均为全双工,即读写可以同时进行
  • 虽然 Stream 是单向流动的,但是它也是全双工的

2、IO 模型

  • 同步:线程自己去获取结果(一个线程)
    • 例如:线程调用一个方法后,需要等待方法返回结果
  • 异步:线程自己不去获取结果,而是由其它线程返回结果(至少两个线程)
    • 例如:线程 A 调用一个方法后,继续向下运行,运行结果由线程 B 返回

当调用一次 channel**.read** 或 stream**.read** 后,会由用户态切换至操作系统内核态来完成真正数据读取,而读取又分为两个阶段,分别为:

  • 等待数据阶段
  • 复制数据阶段

根据 UNIX 网络编程 - 卷 I,IO 模型主要有以下几种

阻塞 IO

 

  • 用户线程进行 read 操作时,需要等待操作系统执行实际的 read 操作,此期间用户线程是被阻塞的,无法执行其他操作

非阻塞 IO

 

  • 用户线程

    在一个循环中一直调用 read 方法,若内核空间中还没有数据可读,立即返回

    • 只是在等待阶段非阻塞
  • 用户线程发现内核空间中有数据后,等待内核空间执行复制数据,待复制结束后返回结果

多路复用

 

Java 中通过 Selector 实现多路复用

  • 当没有事件是,调用 select 方法会被阻塞住
  • 一旦有一个或多个事件发生后,就会处理对应的事件,从而实现多路复用

多路复用与阻塞 IO 的区别

  • 阻塞 IO 模式下,若线程因 accept 事件被阻塞,发生 read 事件后,仍需等待 accept 事件执行完成后,才能去处理 read 事件
  • 多路复用模式下,一个事件发生后,若另一个事件处于阻塞状态,不会影响该事件的执行

异步 IO

 

  • 线程 1 调用方法后理解返回,不会被阻塞也不需要立即获取结果
  • 当方法的运行结果出来以后,由线程 2 将结果返回给线程 1

3、零拷贝

零拷贝指的是数据无需拷贝到 JVM 内存中,同时具有以下三个优点

  • 更少的用户态与内核态的切换
  • 不利用 cpu 计算,减少 cpu 缓存伪共享
  • 零拷贝适合小文件传输

传统 IO 问题

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

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

byte[] buf = new byte[(int)f.length()];
file.read(buf);

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

内部工作流如下

 

  • Java 本身并不具备 IO 读写能力,因此 read 方法调用后,要从 Java 程序的用户态切换至内核态,去调用操作系统(Kernel)的读能力,将数据读入内核缓冲区。这期间用户线程阻塞,操作系统使用 DMA(Direct Memory Access)来实现文件读,其间也不会使用 CPU

    DMA 也可以理解为硬件单元,用来解放 cpu 完成文件 IO

  • 从内核态切换回用户态,将数据从内核缓冲区读入用户缓冲区(即 byte [] buf),这期间 CPU 会参与拷贝,无法利用 DMA

  • 调用 write 方法,这时将数据从 ** 用户缓冲区(byte [] buf)** 写入 socket 缓冲区,CPU 会参与拷贝

  • 接下来要向网卡写数据,这项能力 Java 又不具备,因此又得从用户态切换至内核态,调用操作系统的写能力,使用 DMA 将 socket 缓冲区的数据写入网卡,不会使用 CPU

可以看到中间环节较多,java 的 IO 实际不是物理设备级别的读写,而是缓存的复制,底层的真正读写是操作系统来完成的

  • 用户态与内核态的切换发生了 3 次,这个操作比较重量级
  • 数据拷贝了共 4 次

NIO 优化

通过 DirectByteBuf

  • ByteBuffer.allocate(10)
    • 底层对应 HeapByteBuffer,使用的还是 Java 内存
  • ByteBuffer.allocateDirect(10)
    • 底层对应 DirectByteBuffer,使用的是操作系统内存

大部分步骤与优化前相同,唯有一点 **:Java 可以使用 DirectByteBuffer 将堆外内存映射到 JVM 内存中来直接访问使用 **

  • 这块内存不受 JVM 垃圾回收的影响,因此内存地址固定,有助于 IO 读写
  • Java 中的 DirectByteBuf 对象仅维护了此内存的虚引用,内存回收分成两步
    • DirectByteBuffer 对象被垃圾回收,将虚引用加入引用队列
      • 当引用的对象 ByteBuffer 被垃圾回收以后,虚引用对象 Cleaner 就会被放入引用队列中,然后调用 Cleaner 的 clean 方法来释放直接内存
      • DirectByteBuffer 的释放底层调用的是 Unsafe 的 freeMemory 方法
    • 通过专门线程访问引用队列,根据虚引用释放堆外内存
  • 减少了一次数据拷贝,用户态与内核态的切换次数没有减少

进一步优化 1

以下两种方式都是零拷贝,即无需将数据拷贝到用户缓冲区中(JVM 内存中)

底层采用了 linux 2.1 后提供的 sendFile 方法,Java 中对应着两个 channel 调用 transferTo/transferFrom 方法拷贝数据

 

  • Java 调用 transferTo 方法后,要从 Java 程序的用户态切换至内核态,使用 DMA 将数据读入内核缓冲区,不会使用 CPU
  • 数据从内核缓冲区传输到 socket 缓冲区,CPU 会参与拷贝
  • 最后使用 DMA 将 socket 缓冲区的数据写入网卡,不会使用 CPU

这种方法下

  • 只发生了 1 次用户态与内核态的切换
  • 数据拷贝了 3 次

进一步优化 2

linux 2.4 对上述方法再次进行了优化

 

  • Java 调用 transferTo 方法后,要从 Java 程序的用户态切换至内核态,使用 DMA 将数据读入内核缓冲区,不会使用 CPU
  • 只会将一些 offset 和 length 信息拷入 socket 缓冲区,几乎无消耗
  • 使用 DMA 将 内核缓冲区的数据写入网卡,不会使用 CPU

整个过程仅只发生了 1 次用户态与内核态的切换,数据拷贝了 2 次

4、AIO

AIO 用来解决数据复制阶段的阻塞问题

  • 同步意味着,在进行读写操作时,线程需要等待结果,还是相当于闲置
  • 异步意味着,在进行读写操作时,线程不必等待结果,而是将来由操作系统来通过回调方式由另外的线程来获得结果

异步模型需要底层操作系统(Kernel)提供支持

  • Windows 系统通过 IOCP 实现了真正的异步 IO
  • Linux 系统异步 IO 在 2.6 版本引入,但其底层实现还是用多路复用模拟了异步 IO,性能没有优势

与[转帖]IO、NIO、BIO 傻傻分不清吗,让我对象告诉你~~相似的内容:

[转帖]IO、NIO、BIO 傻傻分不清吗,让我对象告诉你~~

https://my.oschina.net/jiagoushi/blog/5783304 1、Stream 与 Channel stream 不会自动缓冲数据,channel 会利用系统提供的发送缓冲区、接收缓冲区(更为底层) stream 仅支持阻塞 API,channel 同时支持阻塞、非阻塞

【转帖】47.直接内存

目录 1.直接内存概述2.`IO`与`NIO`对比3.直接内存的`OOM`与内存大小设置 1.直接内存概述 1.直接内存不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。 2.直接内存是在Java堆外,直接向系统申请的内存空间 3.Java的NIO库允许使用直接内存,用于数据

[转帖]IO虚拟化——SR-IOV 原理

摘要: 介绍SR-IOV 的概念、使用场景、VMware 和 KVM 中的配置方法。 第一部分:虚拟化通信延迟: 第二部分:IO 虚拟化 第三部分:SR-IOV 第四部分:Intel网卡在VMware 环境 实现 SR-IOV 需要的条件和配置方法 第五部分:SR-IOV 在Red Hat Virt

[转帖]io 性能指标及其基准测试

https://www.jianshu.com/p/23a956e09b1c 磁盘io性能指标 主要有2个: IOPSIOPS (Input/Output Per Second)即每秒的输入输出量(或读写次数),是衡量磁盘性能的主要指标之一。IOPS是指单位时间内系统能处理的I/O请求数量,I/O请

[转帖]IO测试工具之fio详解

目前主流的第三方IO测试工具有fio、iometer和Orion,这三种工具各有千秋。 fio在Linux系统下使用比较方便,iometer在window系统下使用比较方便,Orion是oracle的IO测试软件,可在没有安装oracle数据库的情况下模拟oracle数据库场景的读写。 如下是在Li

[转帖]IO多路复用的三种机制Select,Poll,Epoll

I/O多路复用(multiplexing)的本质是通过一种机制(系统内核缓冲I/O数据),让单个进程可以监视多个文件描述符,一旦某个描述符就绪(一般是读就绪或写就绪),能够通知程序进行相应的读写操作 select、poll 和 epoll 都是 Linux API 提供的 IO 复用方式。 相信大家

【转帖】io_uring vs epoll ,谁在网络编程领域更胜一筹?

简介:从定量分析的角度,通过量化 io_uring 和 epoll 两种编程框架下的相关操作的耗时,来分析二者的性能差异。 本文作者:王小光,「高性能存储技术SIG」核心成员。 背景 io_uring 在传统存储 io 场景已经证明其价值,但 io_uring 不仅支持传统存储 io,也支持网络 i

【转帖】io_uring vs epoll ,谁在网络编程领域更胜一筹?

io_uring vs epoll ,谁在网络编程领域更胜一筹? 2021-12-16 1473举报 简介: 从定量分析的角度,通过量化 io_uring 和 epoll 两种编程框架下的相关操作的耗时,来分析二者的性能差异。 3.jpg 本文作者:王小光,「高性能存储技术SIG」核心成员。 背景

[转帖]硬盘IO性能

https://juejin.cn/post/6844904088715411463 我们大部分时间都是在开发应用系统,当我们的功能实现时和实现后,我们可能会经常的思考或者讨论关于性能方面的问题,性能优化也有很多个方面,那么我们今天主要来一起探讨一下IO性能。 谈到IO性能,我们就会联想到我们电脑的

[转帖]高性能IO模型:为什么单线程Redis能那么快?

https://zhuanlan.zhihu.com/p/596170085 你好,我是蒋德钧。 今天,我们来探讨一个很多人都很关心的问题:“为什么单线程的Redis能那么快?” 首先,我要和你厘清一个事实,我们通常说,Redis是单线程,主要是指Redis的网络IO和键值对读写是由一个线程来完成的