文章目錄
- 零、為什麼需要`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
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
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操作
- 程序執行 I/O 操作, 向作業系統發送請求, 如
系統調用, 要求其将緩沖區填滿read()
- 核心随即向磁盤控制硬體發出指令, 要求從磁盤讀取資料
- 磁盤控制器把資料直接寫入核心記憶體緩沖區, 這一步通過
完成, 無虛 CPUDMA
- 一旦磁盤控制器把緩沖區裝滿, 核心随即把資料從核心空間的臨時緩沖區拷貝到程序執行
調用時指定的緩沖區read()
I/O緩沖區操作簡圖:
2. 使用者空間與核心空間
- 使用者空間是正常程序所在區域
JVM
就是正常程序, 駐守于使用者空間
使用者空間是非特權區域, 比如: 在該區域執行的代碼就不能直接通路硬體裝置
- 核心空間就是作業系統所在區域
核心有特别的權利: 它能與裝置控制器通訊, 控制着使用者區域程序的運作狀态.
- 為什要把資料從核心空間拷貝到使用者空間?
- 硬體通常不能直接通路使用者空間
- 磁盤這種基于塊存儲的硬體裝置操作的是固定大小的資料塊, 而使用者程序請求的可能是任意大小的或非對齊的資料塊
- 在資料往來于使用者空間與儲存設備的過程中, 核心負責資料的分解、再組合工作, 是以充當着中間人的角色
-
發散 / 彙聚
為了高效操作, 程序隻需一個系統調用, 就能把一連串緩沖區位址傳遞給作業系統
如圖:
(2) 虛拟記憶體
虛拟記憶體: 使用虛拟位址取代實體(硬體 RAM)記憶體位址
好處如下:
- 一個以上的虛拟位址可指向同一個實體記憶體位址
- 虛拟記憶體空間可大于實際可用的硬體記憶體
因為裝置控制器不能通過 DMA 直接存儲到使用者空間
若把核心空間位址與使用者空間的虛拟位址映射到同一個實體位址, 這樣, DMA 硬體(隻能通路實體記憶體位址) 就可以填充對核心與使用者空間程序同時可見的緩沖區
這樣做, 可以省去核心與使用者空間的往來拷貝
前提:
核心與使用者緩沖區必須使用相同的頁對齊, 緩沖區的大小還必須是磁盤控制器大小 (通常為 512 位元組的磁盤扇區) 的倍數
記憶體空間多重映射圖:
(3) 檔案 I/O
1. 記憶體映射檔案
大多數作業系統都支援的特殊類型的 I/O 操作, 允許使用者程序最大限度地利用面向頁的系統 I/O 特性, 并完全摒棄緩沖區拷貝
使用者記憶體到檔案系統頁的映射圖:
好處:
- 使用者程序把檔案資料當作記憶體,是以無需釋出 read( )或 write( )系統調用。
- 當使用者程序碰觸到映射記憶體空間,頁錯誤會自動産生,進而将檔案資料從磁盤讀進記憶體。如果使用者修改了映射記憶體空間,相關頁會自動标記為髒,随後重新整理到磁盤,檔案得到更新。
- 作業系統的虛拟記憶體子系統會對頁進行智能高速緩存,自動根據系統負載進行記憶體管理
- 資料總是按頁對齊的,無需執行緩沖區拷貝。
- 大型檔案使用映射,無需耗費大量記憶體,即可進行資料拷貝。
三、參考資料
- http://ifeve.com/java-nio-vs-io/
- << Java NIO >>