Unveiling Java’s IO Layers: From BIO to Netty – A System Call Deep Dive

This article traces the real system‑call flow of Java’s I/O models—BIO, NIO, Netty and AIO—on CentOS 7.5, compares their performance, lists advantages and disadvantages, and shows how each model interacts with the Linux kernel via strace.

Alibaba Cloud Developer
Alibaba Cloud Developer
Alibaba Cloud Developer
Unveiling Java’s IO Layers: From BIO to Netty – A System Call Deep Dive

Preface

This article investigates the actual system‑call flow of Java I/O on a CentOS 7.5 system, using strace to trace BIO, NIO, Netty and AIO implementations.

1 BIO (Blocking I/O)

Shows a simple blocking socket server, the socket, bind, listen, accept and recvfrom calls, and explains that each client requires a dedicated thread, leading to the C10K problem.

Advantages : simple code, clear logic. Disadvantages : one thread per connection, blocking reads.

public class BIOSocket {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8090);
        System.out.println("step1: new ServerSocket ");
        while (true) {
            Socket client = serverSocket.accept();
            System.out.println("step2: client\t" + client.getPort());
            new Thread(() -> {
                try {
                    InputStream in = client.getInputStream();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                    while (true) {
                        System.out.println(reader.readLine());
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

2 NIO (Non‑blocking I/O)

Rewrites the server with ServerSocketChannel and Selector, configuring channels as non‑blocking and handling reads with ByteBuffer. System‑call trace shows socket, bind, listen, fcntl, and repeated read returning EAGAIN.

Advantages : far fewer threads, no per‑connection blocking. Disadvantages : manual byte‑level protocol handling, higher CPU usage for many connections.

public class NIOSocket {
    private static LinkedList<SocketChannel> clients = new LinkedList<>();
    public static void main(String[] args) throws IOException {
        ServerSocketChannel socketChannel = ServerSocketChannel.open();
        socketChannel.bind(new InetSocketAddress(9090));
        socketChannel.configureBlocking(true);
        startClientChannelHandleThread();
        while (true) {
            SocketChannel client = socketChannel.accept();
            if (client != null) {
                client.configureBlocking(false);
                System.out.println("client port :" + client.socket().getPort());
                clients.add(client);
            }
        }
    }
    private static void startClientChannelHandleThread() {
        new Thread(() -> {
            while (true) {
                ByteBuffer buffer = ByteBuffer.allocateDirect(4096);
                for (SocketChannel c : clients) {
                    int num = 0;
                    try { num = c.read(buffer); } catch (IOException e) { e.printStackTrace(); }
                    if (num > 0) {
                        buffer.flip();
                        byte[] clientBytes = new byte[buffer.limit()];
                        buffer.get(clientBytes);
                        System.out.println(c.socket().getPort() + ":" + new String(clientBytes));
                        buffer.clear();
                    }
                }
            }
        }).start();
    }
}

3 Multiplexing (select / poll / epoll)

Uses Java’s Selector (epoll on Linux) to let the kernel report ready sockets, reducing system‑call overhead compared with BIO and NIO. Includes a trace of epoll_create, epoll_ctl, epoll_wait, and accept. Epoll provides O(1) scalability versus poll’s O(n).

Selector selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (selector.select() > 0) {
    Set<SelectionKey> selectionKeys = selector.selectedKeys();
    Iterator<SelectionKey> iter = selectionKeys.iterator();
    while (iter.hasNext()) {
        SelectionKey key = iter.next();
        iter.remove();
        if (key.isAcceptable()) {
            SocketChannel client = socketChannel.accept();
            client.configureBlocking(false);
            client.register(selector, SelectionKey.OP_READ);
            System.out.println("new client : " + client.getRemoteAddress());
        } else if (key.isReadable()) {
            readDataFromSocket(key);
        }
    }
}

4 Netty

Provides a minimal Netty telnet server example (bootstrap, boss/worker groups, channel pipeline). Highlights Netty’s built‑in encoders/decoders, thread‑pool model and ByteBuf pooling, which decouple business logic from network handling.

Advantages : rich protocol support, clean separation of concerns, high performance. Disadvantages : steeper learning curve.

public final class TelnetServer {
    static final boolean SSL = System.getProperty("ssl") != null;
    static final int PORT = Integer.parseInt(System.getProperty("port", SSL ? "8992" : "8023"));
    public static void main(String[] args) throws Exception {
        final SslContext sslCtx;
        if (SSL) {
            SelfSignedCertificate ssc = new SelfSignedCertificate();
            sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
        } else {
            sslCtx = null;
        }
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class)
             .handler(new LoggingHandler(LogLevel.INFO))
             .childHandler(new TelnetServerInitializer(sslCtx));
            b.bind(PORT).sync().channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

5 AIO

Shows Java AIO using an epoll‑based Proactor pattern: a boss thread accepts connections, worker threads process completed I/O events. System‑call trace includes epoll_create, epoll_ctl, read, and thread cloning.

Advantages : asynchronous API hides thread management. Disadvantages : potential concurrency issues in thread‑pool handling, less friendly buffer API.

epoll_create(256) = 5
epoll_ctl(5, EPOLL_CTL_ADD, 6, {EPOLLIN, {u32=6, u64=11590018039084482566}}) = 0
clone(... ) = 22704   // boss thread (Proactor)
... // boss waits on epoll, accepts, clones worker threads, reads data, etc.

Conclusion

From a system‑call perspective, Netty combines epoll‑level efficiency with high‑level abstractions, while BIO suffers from thread explosion, NIO reduces threads but still requires manual parsing, and AIO offers true async at the cost of added complexity.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

javanioNettyioSystem CallsAIO
Alibaba Cloud Developer
Written by

Alibaba Cloud Developer

Alibaba's official tech channel, featuring all of its technology innovations.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.