当前位置: 首页>编程语言>正文

【Java NIO】 之 NIO 与 IO

文章目录

  • ​​零、为什么需要`Java NIO`​​
  • ​​原因​​
  • ​​一、`Java NIO` 与 `IO` 区别​​
  • ​​(1) 面向流 与 面向缓冲​​
  • ​​(2) 阻塞 与 非阻塞IO​​
  • ​​1. 阻塞​​
  • ​​2. 非阻塞​​
  • ​​(3) 选择器(Selector)​​
  • ​​二、I/O 概念​​
  • ​​(1) 缓冲区​​
  • ​​1. I/O操作​​
  • ​​2. 用户空间与内核空间​​
  • ​​(2) 虚拟内存​​
  • ​​(3) 文件 I/O​​
  • ​​1. 内存映射文件​​
  • ​​三、参考资料​​


零、为什么需要​​Java NIO​


早期关注点在与 ​​JVM​​​, 而现在 ​​JVM​​​ 运行字节码的速率已经接近本地编译代码, 借助动态运行时优化, 其表现甚至还有所超越.
意味着: 多数 Java 应用程序已不再受 CPU 的束缚(把大量时间用在执行代码上), 而更改多时候是受 I/O 的束缚 (等待数据传输)

原因

操作系统 与 Java 基于流的I/O模型有些不匹配

操作系统要移动的是大块数据 (缓冲区), 这往往是在硬件直接存储器存取 (DMA) 的协助下完成的

​JVM​​​的​​I/O​​类喜欢操作小块数据 — 单个字节、几行文本

结果是: 操作系统送来整个缓冲区的数据, ​​Java IO​​的流数据类再花大量时间把它们拆成小块, 往往拷贝一小块就要往返于几层对象

即: 操作系统喜欢整卡车运来数据, ​​Java IO​​类 则喜欢一铲子一铲子地加工数据

SO: 有了 ​​NIO​​, 就可以轻松地把一卡车数据备份到能直接使用的地方 (ByteBuffer)

Tips: 这里的备份, 需要注意下, 之后会有优化, 如 零拷贝(zero copy)


一、​​Java NIO​​​ 与 ​​IO​​ 区别


​Java NIO​​​ : 面向缓冲、非阻塞IO、选择器
​​​IO​​ : 面向流、 阻塞IO、 无


(1) 面向流 与 面向缓冲

面向流: 每次从流中读一个或多个字节, 直至读取所有字节, 它们没有被缓存在任何地方, 不能前后移动流中的数据

面向缓冲: 数据读取到一个它稍后处理的缓冲区, 需要时可在缓冲区中前后移动, 增加了处理过程中的灵活性


(2) 阻塞 与 非阻塞IO


1. 阻塞

​Java IO​​的各种流是阻塞的

当一个线程调用​​read()​​​ 或 ​​write()​​时, 该线程被阻塞, 直到有一些数据被读取, 或数据完全写入, 期间该线程不能再干任何事情了


2. 非阻塞

一个线程从某个通道发送请求读取数据, 若有数据则读取, 若无则继续做其他的事情.

线程通常将非阻塞IO的空闲时间用于在其他通道上执行IO操作, 所有一个单独的线程可以管理多个输入和输出通道 (Channel)


(3) 选择器(Selector)

​Java NIO​​ 的 选择器允许一个单独的线程来监视多个输入通道


二、I/O 概念


(1) 缓冲区


1. I/O操作
  1. 进程执行 I/O 操作, 向操作系统发送请求, 如​​read()​​系统调用, 要求其将缓冲区填满
  2. 内核随即向磁盘控制硬件发出命令, 要求从磁盘读取数据
  3. 磁盘控制器把数据直接写入内核内存缓冲区, 这一步通过​​DMA​​ 完成, 无虚 CPU
  4. 一旦磁盘控制器把缓冲区装满, 内核随即把数据从内核空间的临时缓冲区拷贝到进程执行​​read()​​调用时指定的缓冲区

I/O缓冲区操作简图:


2. 用户空间与内核空间
  1. 用户空间是常规进程所在区域

​JVM​​​ 就是常规进程, 驻守于用户空间
用户空间是非特权区域, 比如: 在该区域执行的代码就不能直接访问硬件设备

  1. 内核空间就是操作系统所在区域

内核有特别的权利: 它能与设备控制器通讯, 控制着用户区域进程的运行状态.

  1. 为什要把数据从内核空间拷贝到用户空间?
  1. 硬件通常不能直接访问用户空间
  2. 磁盘这种基于块存储的硬件设备操作的是固定大小的数据块, 而用户进程请求的可能是任意大小的或非对齐的数据块
  3. 在数据往来于用户空间与存储设备的过程中, 内核负责数据的分解、再组合工作, 因此充当着中间人的角色
  1. 发散 / 汇聚
    为了高效操作, 进程只需一个系统调用, 就能把一连串缓冲区地址传递给操作系统

如图:


(2) 虚拟内存

虚拟内存: 使用虚拟地址取代物理(硬件 RAM)内存地址
好处如下:

  1. 一个以上的虚拟地址可指向同一个物理内存地址
  2. 虚拟内存空间可大于实际可用的硬件内存

因为设备控制器不能通过 DMA 直接存储到用户空间

若把内核空间地址与用户空间的虚拟地址映射到同一个物理地址, 这样, DMA 硬件(只能访问物理内存地址) 就可以填充对内核与用户空间进程同时可见的缓冲区

这样做, 可以省去内核与用户空间的往来拷贝

前提:
内核与用户缓冲区必须使用相同的页对齐, 缓冲区的大小还必须是磁盘控制器大小 (通常为 512 字节的磁盘扇区) 的倍数

内存空间多重映射图:


(3) 文件 I/O


1. 内存映射文件

大多数操作系统都支持的特殊类型的 I/O 操作, 允许用户进程最大限度地利用面向页的系统 I/O 特性, 并完全摒弃缓冲区拷贝

用户内存到文件系统页的映射图:

好处:

  1. 用户进程把文件数据当作内存,所以无需发布 read( )或 write( )系统调用。
  2. 当用户进程碰触到映射内存空间,页错误会自动产生,从而将文件数据从磁盘读进内存。如果用户修改了映射内存空间,相关页会自动标记为脏,随后刷新到磁盘,文件得到更新。
  3. 操作系统的虚拟内存子系统会对页进行智能高速缓存,自动根据系统负载进行内存管理
  4. 数据总是按页对齐的,无需执行缓冲区拷贝。
  5. 大型文件使用映射,无需耗费大量内存,即可进行数据拷贝。

三、参考资料


  1. ​​http://ifeve.com/java-nio-vs-io/​​
  2. << Java NIO >>



https://www.xamrdz.com/lan/5ax1967450.html

相关文章: