The Hidden Cost of System Calls (And How Buffering Fixes It)

January 18, 2026byRohit Roy

Here is some code that reads data from a file:

InputStream input = socket.getInputStream();
int byte1 = input.read();
int byte2 = input.read();
int byte3 = input.read();

It looks simple. But it is incredibly slow. Not because the algorithm is wrong. Because of how programs interact with the operating system.

The System Call

Applications run in a restricted area of memory called user space. Programs cannot directly touch hardware like disk drives or network cards. The operating system controls all of that from kernel space.

When your program needs data from a file or socket, it has to ask the OS. Calling read() triggers a system call. The CPU switches your process from user mode to kernel mode. The OS does its work. Then it switches back to user mode and returns the result.

This mode switch costs roughly 100 to 500 nanoseconds on modern hardware. That sounds small. But reading a 1MB file one byte at a time means 1,048,576 system calls. The overhead alone makes it unusably slow.

Buffering

The fix is simple. Instead of asking the OS for 1 byte at a time, ask for a large chunk once. Store it in memory. Serve all future reads from that chunk.

Context Switch Buffering

The first read() call fetches 8KB from the OS in one system call. The next 8,191 reads pull bytes directly from memory. No system calls. When the buffer runs out, one more system call refills it.

Here is what that looks like internally:

public class BufferedReader {
    private char[] buffer = new char[8192];
    private int position = 0;
    private int limit = 0;

    public int read() {
        if (position >= limit) {
            fillBuffer();  // system call happens here
        }
        return buffer[position++];
    }

    private void fillBuffer() {
        limit = underlyingReader.read(buffer, 0, 8192);
        position = 0;
    }
}

The buffer is a plain array in heap memory. Reading from it is an array access. That takes nanoseconds.

Every serious I/O library buffers by default. Now you know why.