在高性能網絡編程 (Netty、Kafka、RocketMQ 底層) 與大文件處理領域,Java NIO (New I/O) 是繞不開的核心技術。相較於傳統 BIO (Blocking I/O) 的流式、阻塞、字節級操作,NIO 引入 Channel (通道)、Buffer (緩衝區)、Selector (多路複用器) 三大核心組件,實現了基於塊的非阻塞 I/O,大幅提升系統吞吐量。本文從底層原理到代碼實戰,全景式拆解 NIO 關鍵技術點。
| 維度 | BIO (Stream) | NIO (Channel/Buffer) |
|---|---|---|
| 數據單位 | 字節/字符流 | 塊 |
| 讀寫模式 | 阻塞 | 非阻塞 + 選擇器 |
| 方向性 | 單向 | 雙向 |
| 內存模型 | 堆內 byte[] | 堆內/直接內存 |
| 文件操作 | 流式拷貝 | 內存映射 / 零拷貝 |

position (下一個讀寫位置)、limit (邊界)、capacity (容量)。public static void copy(String src, String dest) throws IOException {
try (FileInputStream fis = new FileInputStream(src);
FileOutputStream fos = new FileOutputStream(dest);
FileChannel in = fis.getChannel();
FileChannel out = fos.getChannel()) {
ByteBuffer buf = ByteBuffer.allocateDirect(1024 * 64); // 64KB 直接內存
while (in.read(buf) != -1) {
buf.flip(); // 切換讀模式
out.write(buf); // 寫入
buf.clear(); // 切換寫模式,position=0, limit=capacity
}
}
}
關鍵點: flip() 與 clear()/compact() 的正確配合是 Buffer 操作的核心,錯誤會導致數據截斷或覆蓋。

ByteBuffer buf = ByteBuffer.allocate(15); // 初始:pos=0, lim=15, cap=15 buf.put((byte)1).put((byte)2); // 寫入 2 個 // 狀態:pos=2, lim=15, cap=15 buf.flip(); // 切換讀模式 // 狀態:pos=0, lim=2, cap=15 buf.get(); // 讀 1 個 // 狀態:pos=1, lim=2, cap=15 buf.compact(); // 未讀數據移至頭部,切換寫模式 // 狀態:pos=1, lim=15, cap=15 (未讀的 1 個字節保留在 index 0) buf.put((byte)3); // 續寫 // 狀態:pos=2, lim=15, cap=15
三大重置方法對比:

flip():寫→讀,limit=position, position=0。rewind():重讀,position=0,limit 不變。clear():清空準備寫,position=0, limit=capacity (數據未真刪除)。compact():未讀數據前移,position=未讀長度, limit=capacity (讀寫交替場景首選)。| 特性 | 方法 | 應用場景 |
|---|---|---|
| 標記/復位 | mark() / reset() | 解析器回溯、協議解析預讀 |
| 複製 | duplicate() | 共享底層數據,獨立 position/limit,多線程並行處理同一數據不同視圖 |
| 切片 | slice() | 零拷貝子視圖,處理結構化數據 (如消息頭/體分離) |
| 只讀視圖 | asReadOnlyBuffer() | 安全傳遞給不可信代碼,防範意外修改 |
try (RandomAccessFile raf = new RandomAccessFile("data.dat", "rw");
FileChannel fc = raf.getChannel()) {
MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, raf.length());
// 直接操作 mbb 即操作文件,OS 負責頁面調度
mbb.put(0, (byte) 0xFF); // 修改首字節,自動同步到磁盤
}
原理:OS 將文件頁面映射至進程虛擬地址空間,用戶態直接讀寫物理頁,**省去內核態↔用戶態兩次拷貝**,大文件隨機讀寫性能提升數倍。
ByteBuffer directBuf = ByteBuffer.allocateDirect(1024 * 1024); // 1MB 堆外內存
// 聚集寫入:多個 Buffer 按序寫入 Channel
ByteBuffer header = ByteBuffer.wrap("HEADER".getBytes());
ByteBuffer body = ByteBuffer.wrap("BODY_CONTENT".getBytes());
ByteBuffer[] bufs = {header, body};
channel.write(bufs); // 一次系統調用完成
// 散射讀取:Channel 數據按序填充多個 Buffer
ByteBuffer h = ByteBuffer.allocate(6);
ByteBuffer b = ByteBuffer.allocate(12);
channel.read(new ByteBuffer[]{h, b});
適用於固定格式協議 (如 HTTP Header/Body、自定義二進制幀),避免手動拼接/拆分 ByteBuffer。
Selector + SocketChannel + 直接內存池 (Netty 模式)。FileChannel.map() 內存映射 或 transferTo/transferFrom 零拷貝。Scattering Reads / Gathering Writes。掌握 NIO 三板斧 — **Buffer 狀態機、Channel 雙向非阻塞、直接內存零拷貝** — 是通往高性能 Java 工程師的必經之路。