Backend Development 13 min read

Understanding Zero‑Copy in Java: Concepts, Mechanisms, and Implementations

This article explains the zero‑copy technique in Java, covering I/O fundamentals, kernel read/write flow, mmap+write and Sendfile methods, virtual memory mapping, MappedByteBuffer and DirectByteBuffer usage, channel‑to‑channel transfer, Netty composite buffers, and how frameworks like RocketMQ and Kafka leverage zero‑copy for performance.

Top Architect
Top Architect
Top Architect
Understanding Zero‑Copy in Java: Concepts, Mechanisms, and Implementations

Zero‑copy means data can be transferred between kernel and user space without the usual memory copies, dramatically improving I/O performance. It is widely used in Java NIO, Netty, Kafka, RocketMQ and other high‑throughput frameworks.

I/O concepts : A process issues a read request; the kernel checks its cache, copies data from the kernel buffer to the user buffer, or reads from disk via DMA into the kernel buffer, then copies to the user buffer. This copy step is the target of zero‑copy optimizations.

Zero‑copy mechanisms :

mmap + write – maps a file directly into the process address space, eliminating the kernel‑to‑user copy for reads.

Sendfile – a system call that transfers data from a file descriptor to a socket entirely within kernel space, avoiding both user‑space copies and extra context switches.

Virtual memory allows multiple virtual addresses to map to the same physical memory and lets the address space be larger than physical RAM. By mapping kernel buffers into user space, DMA can write directly to a region visible to both kernel and user processes.

MappedByteBuffer (Java NIO) provides the map() method to create a memory‑mapped file region. The buffer behaves like a ByteBuffer whose data resides on disk; get() reads from the file, put() writes back. Example:

public class MappedByteBufferTest {
    public static void main(String[] args) throws Exception {
        File file = new File("D://db.txt");
        long len = file.length();
        byte[] ds = new byte[(int) len];
        MappedByteBuffer mappedByteBuffer = new FileInputStream(file)
            .getChannel().map(FileChannel.MapMode.READ_ONLY, 0, len);
        for (int offset = 0; offset < len; offset++) {
            byte b = mappedByteBuffer.get();
            ds[offset] = b;
        }
        Scanner scan = new Scanner(new ByteArrayInputStream(ds)).useDelimiter(" ");
        while (scan.hasNext()) {
            System.out.print(scan.next() + " ");
        }
    }
}

The underlying map() native implementation calculates page alignment, calls map0 , and handles OutOfMemoryError by forcing GC and retrying.

DirectByteBuffer is the concrete class instantiated for a memory‑mapped region; it allocates off‑heap memory that does not consume JVM heap.

Example of allocating off‑heap memory directly:

ByteBuffer directByteBuffer = ByteBuffer.allocateDirect(100);

Channel‑to‑Channel transfer uses FileChannel.transferTo() to move data between channels without an intermediate user buffer:

public class ChannelTransfer {
    public static void main(String[] argv) throws Exception {
        String[] files = new String[1];
        files[0] = "D://db.txt";
        catFiles(Channels.newChannel(System.out), files);
    }
    private static void catFiles(WritableByteChannel target, String[] files) throws Exception {
        for (int i = 0; i < files.length; i++) {
            FileInputStream fis = new FileInputStream(files[i]);
            FileChannel channel = fis.getChannel();
            channel.transferTo(0, channel.size(), target);
            channel.close();
            fis.close();
        }
    }
}

Netty zero‑copy provides CompositeChannelBuffer and Slice buffers so that a large HTTP message can be assembled from multiple underlying buffers without copying data. The buffer stores references to component buffers and calculates indices on the fly.

public class CompositeChannelBuffer extends AbstractChannelBuffer {
    private final ByteOrder order;
    private ChannelBuffer[] components;
    private int[] indices;
    private int lastAccessedComponentId;
    private final boolean gathering;
    public byte getByte(int index) {
        int componentId = componentId(index);
        return components[componentId].getByte(index - indices[componentId]);
    }
    // ...省略...
}

Other frameworks such as RocketMQ and Kafka also rely on zero‑copy: RocketMQ uses mmap + write for consumer responses, while Kafka employs the Sendfile system call to stream data directly from disk to the network.

Conclusion : Zero‑copy eliminates unnecessary memory copies between kernel and user space, reduces context switches, and improves throughput for high‑performance Java applications. By leveraging memory‑mapped files, channel‑to‑channel transfers, and Netty’s composite buffers, developers can build efficient I/O pipelines.

performanceNettyMMAPsendfileZero CopyJava NIO
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

login 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.