天天看點

【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 >>