NIO
Java NIO (New I/O) — Channels, Buffers, Selectors, and when to use NIO over traditional I/O for high-performance network and file operations.
Last reviewed: January 2025
🔍 What Is It?
NIO Core Concepts:
Buffer — a fixed-size container for data
- →ByteBuffer, CharBuffer, IntBuffer, etc.
- →Key properties: capacity (total size), limit (read boundary), position (current read/write point)
- →flip() — switch from write mode to read mode
- →clear() / compact() — prepare for writing again
- →Direct ByteBuffer — off-heap memory, faster for I/O
Channel — bidirectional I/O endpoint
- →FileChannel — file I/O (also supports memory-mapped files)
- →SocketChannel — TCP client
- →ServerSocketChannel — TCP server
- →DatagramChannel — UDP
Selector — multiplex many channels on one thread
- →Non-blocking: one thread handles thousands of connections
- →SelectionKey: CONNECT, ACCEPT, READ, WRITE events
- →Foundation of event-loop frameworks (Netty, Vert.x)
Path/Files (NIO.2, Java 7+)
- →The modern file API (covered in File API topic)
- →Built on NIO concepts but with a higher-level API
🎯 Why Do We Need It?
Traditional I/O (java.io) is blocking:
- →One thread per connection
- →Thread blocks while waiting for data
- →10,000 connections = 10,000 threads = ~10GB memory
NIO is non-blocking:
- →One thread can handle thousands of connections
- →Thread only processes connections that have data ready
- →Selector tells you which channels are ready
- →Foundation of modern high-performance servers
For enterprise developers, NIO is mostly used indirectly through frameworks (Netty, Spring WebFlux). Direct NIO programming is needed for custom protocols and high-performance file operations.
🏗️ What Happens in Real Projects?
NIO in enterprise Java:
- →Netty — built entirely on NIO; powers many high-performance services
- →Spring WebFlux — reactive web framework built on Reactor Netty (NIO)
- →Memory-mapped files — FileChannel.map() for fast large file access
- →File copying — FileChannel.transferTo() for zero-copy file transfer
- →Direct ByteBuffer — off-heap memory for I/O-intensive processing
⚙️ How It Works
Buffer State Machine:
Write mode: position=0, limit=capacity
After write: position=bytesWritten, limit=capacity
flip(): position=0, limit=bytesWritten (ready to read)
After read: position=bytesRead, limit=bytesWritten
clear(): position=0, limit=capacity (ready to write again)
compact(): unread data moved to start, position=unreadBytesNon-blocking Selector Loop:
while (true) {
selector.select(); // blocks until at least one channel is ready
Set<SelectionKey> keys = selector.selectedKeys();
for (SelectionKey key : keys) {
if (key.isAcceptable()) handleAccept(key);
else if (key.isReadable()) handleRead(key);
else if (key.isWritable()) handleWrite(key);
}
keys.clear();
}💻 Example
NIO File Operations and Memory-Mapped Files — Practical NIO usage for high-performance file operations.
🤔 Common Confusions
❓ When should I use NIO directly vs java.io streams?
Use java.io (BufferedReader, Files.lines()) for most file operations — it is simpler and sufficient. Use NIO directly for: memory-mapped files (large file random access), zero-copy file transfers (FileChannel.transferTo), non-blocking network servers. In practice, most enterprise developers never write raw NIO network code — they use Netty or Spring WebFlux which handle NIO internally.
❓ What is the difference between allocate() and allocateDirect()?
ByteBuffer.allocate() creates a buffer on the JVM heap — GC managed, fast allocation. ByteBuffer.allocateDirect() creates a buffer in native memory (off-heap) — not GC managed, slower to allocate but faster for I/O (no copy between JVM heap and OS). Use direct buffers for long-lived buffers used repeatedly for I/O. Use heap buffers for short-lived buffers.
🚨 Common Production Issues
Forgetting to flip() the buffer — writing data then reading without flip() reads from the wrong position. Always flip() after writing before reading.
Direct ByteBuffer memory leak — direct buffers are not GC'd normally. They are freed when the ByteBuffer object is GC'd, but this can be delayed. Use explicit cleaner (Java 9+: ((DirectBuffer) buffer).cleaner().clean()) for large direct buffers.
Selector not clearing selected keys — if you do not call selectedKeys.remove() after processing each key, the same key is processed again in the next iteration.
✅ Best Practices
Use Files utility class for most file operations — reserve raw NIO for performance-critical cases.
Use FileChannel.transferTo() for file copying — it uses zero-copy OS primitives.
Use memory-mapped files for large files with random access patterns.
In production, use Netty instead of raw Selector-based NIO for network servers.
Pre-allocate and reuse ByteBuffers for I/O-intensive code to reduce GC pressure.
🏛️ Architect's Note
For new microservices requiring high-performance I/O, consider Spring WebFlux (built on Reactor Netty/NIO) instead of Spring MVC (blocking I/O). WebFlux handles tens of thousands of concurrent connections with a small thread pool. However, WebFlux requires a fully reactive programming model — blocking code anywhere breaks the model. Java 21 virtual threads with Spring MVC often achieve similar throughput with simpler code.
💬 Frequently Asked Questions
🎯 Key Takeaways
🔗 Related Topics
🎯 Practice this topic in interviews
Core Java Interview Prep
Practice interview questions that connect directly to this technology track.
↗ Where this topic fits
Core Java Technology Guide
Return to the Core Java learning hub.
Backend Engineer Career Path
A structured journey through Core Java, Spring Boot, production backend engineering, and interview preparation.
Full Stack Java Developer Career Path
A guided route through frontend foundations, Java backend engineering, delivery workflows, and interview readiness.
Sources
Sources / References
Use these Core Java references to verify the concept in official docs and continue into the most relevant SplashRide pages.
Official documentation
Related SplashRide pages
SplashRide Core Java learning path
Browse the full Core Java topic collection and learning order on SplashRide.
Core Java Interview Prep
Practice Core Java interview questions and scenario-based answers.
Backend Engineer Career Path
A structured journey through Core Java, Spring Boot, production backend engineering, and interview preparation.
Full Stack Java Developer Career Path
A guided route through frontend foundations, Java backend engineering, delivery workflows, and interview readiness.
File API
Java file I/O — the modern java.nio.file.Files API vs legacy java.io.File, reading, writing, and traversing the filesystem.
Try Catch
Java exception handling — try-catch-finally, multi-catch, try-with-resources, and the patterns senior developers use to write resilient enterprise code.