掌握 Channel/Buffer 只是 NIO 入門。本文系統講解 Selector 多路複用、文件鎖與內存映射機制、Scatter/Gather 分散聚集、虛擬內存與頁面調度底層原理,打通「Java API → OS 內核 → 硬件」完整鏈路。
OP_ACCEPT, OP_CONNECT, OP_READ, OP_WRITE)。selector.select() 阻塞直到有就緒事件;selectNow() 非阻塞輪詢;select(timeout) 超時返回。selector.selectedKeys() 遍歷處理,處理完必須 remove() 防止重複。// 主 Reactor 線程
while (!Thread.interrupted()) {
selector.select();
for (SelectionKey key : selector.selectedKeys()) {
if (key.isAcceptable()) accept(key);
else if (key.isReadable()) read(key);
// write 事件通常按需註冊/註銷
}
selector.selectedKeys().clear();
}
Netty 以此為基礎演進出多 Reactor、EventLoop、Pipeline 架構。
FileChannel fc = FileChannel.open(path, READ, WRITE);
FileLock lock = fc.lock(0, Long.MAX_VALUE, false); // 獨占鎖
try { /* 臨界區 */ } finally { lock.release(); }
MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, fileSize);
// 直接像操作內存一樣操作文件
mbb.putInt(0, 123); // 即時同步到磁盤(由 OS 頁面刷新策略決定)
map 參數 position/size)。mbb.force() + 反射調用 Cleaner;舊版本僅靠 GC。// 讀取:數據按序填充多個 Buffer(如 HTTP 頭+體)
ByteBuffer[] bufs = { headerBuf, bodyBuf };
channel.read(bufs);
// 寫入:多個 Buffer 按序聚合發送
channel.write(bufs);
內核優勢:單次系統調用傳遞多個緩衝區地址,內核依次填充/讀取,減少用戶/內核態切換;多 CPU 可並行 DMA。
mmap:磁盤 → 內核緩衝區(映射用戶虛擬地址) → 內核 Socket 緩衝區(3 拷貝)。sendfile:磁盤 → 內核緩衝區 → 內核 Socket 緩衝區(2 拷貝,DMA + CPU 拷貝)。sendfile + DMA gather(Linux 2.4+):磁盤 → 內核緩衝區 → 協議棧(1 拷貝,全 DMA,需網卡支援)。Java FileChannel.transferTo() 底層自動選擇最優系統調用。
mmap/DMA 失敗。java.nio.charset.Charset:編解碼器(CharsetEncoder/Decoder),支援自定義映射表。java.util.regex:Perl 風格正則,Pattern/Matcher,NIO 文件處理常用於日誌解析。Selector 解決「多連接管理」;文件鎖/映射解決「共享存取與大文件隨機訪問」;Scatter/Gather 與零拷貝解決「高吞吐傳輸」。理解虛擬內存與頁面機制,是從「會用 API」進化到「能調優、能排查」的分水嶺。