Zero‑Copy Techniques in Java: I/O Concepts, mmap, Sendfile, and Netty
This article explains the zero‑copy principle, describes core I/O concepts such as buffers and virtual memory, and details Java implementations including mmap + write, Sendfile, MappedByteBuffer, DirectByteBuffer, channel‑to‑channel transfer, and Netty’s composite buffers for high‑performance data transfer.
Zero‑copy means data can be transferred without the repeated copying between user space and kernel space, dramatically improving system performance. It is widely used in Java frameworks like NIO, Netty, Kafka and RocketMQ.
I/O Concepts – All I/O operations move data in and out of buffers. A read request first checks if the kernel already holds the needed data; if not, the kernel reads from disk via DMA directly into a kernel buffer, then copies to the user buffer. Write operations copy user data into a kernel socket buffer before DMA sends it to the network.
Virtual Memory – Modern OSes map virtual addresses to physical memory, allowing multiple virtual addresses to refer to the same physical page and enabling buffers that are visible to both kernel and user space, which is the basis for zero‑copy.
Zero‑Copy Methods
mmap + write – Memory‑maps a file into the process address space, eliminating the kernel‑to‑user copy for reads. The kernel still copies from the read buffer to the socket buffer.
Sendfile – Introduced in Linux 2.1, it transfers data directly from a file descriptor to a socket descriptor inside the kernel, removing one copy and reducing context switches.
Java Zero‑Copy
MappedByteBuffer – Obtained via FileChannel.map(), it creates a memory‑mapped region that can be accessed as a ByteBuffer. 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;
}
// further processing …
}
}The map() method signature is:
public abstract MappedByteBuffer map(FileChannel.MapMode mode, long position, long size) throws IOException;Parameters:
MapMode – READ_ONLY, READ_WRITE, or PRIVATE.
Position – Starting byte offset in the file.
Size – Number of bytes to map.
DirectByteBuffer – The concrete class instantiated for a memory‑mapped buffer; it allocates off‑heap memory that does not consume JVM heap.
Manual off‑heap allocation example:
ByteBuffer directByteBuffer = ByteBuffer.allocateDirect(100);Channel‑to‑Channel Transfer – FileChannel.transferTo() moves bytes directly between channels without an intermediate user buffer:
public class ChannelTransfer {
public static void main(String[] args) throws Exception {
String[] files = {"D://db.txt"};
catFiles(Channels.newChannel(System.out), files);
}
private static void catFiles(WritableByteChannel target, String[] files) throws Exception {
for (String f : files) {
FileInputStream fis = new FileInputStream(f);
FileChannel channel = fis.getChannel();
channel.transferTo(0, channel.size(), target);
channel.close();
fis.close();
}
}
}Signature:
public abstract long transferTo(long position, long count, WritableByteChannel target) throws IOException;Netty Zero‑Copy – Netty provides composite and slice buffers that reference existing buffers, avoiding data copies when assembling or disassembling HTTP messages. The CompositeChannelBuffer holds references to component buffers and calculates indices on‑the‑fly, achieving zero‑copy.
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 systems such as RocketMQ and Kafka also rely on mmap + write or Sendfile to achieve zero‑copy for message persistence and network transmission.
Conclusion – Zero‑copy in Java works by sharing references to the same underlying memory region, eliminating redundant copies and reducing CPU and memory overhead, which is essential for high‑throughput network and storage applications.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
