Start with the ordinary buffered path
On a typical Linux buffered read, the kernel finds file data in its page cache or brings it there from storage. A read() call then copies requested bytes into a buffer in the application's address space. The application calls write() or send() on a socket, and the kernel obtains those bytes again for the networking path.
A simplified diagram often shows disk-to-kernel, kernel-to-user, user-to-kernel, and kernel-to-network. That teaches the main idea, but modern devices use DMA and the details of packet assembly, scatter-gather lists, and copies vary. Don't count every arrow in a diagram as one obligatory CPU copy. The important avoidable part is bouncing an unchanged payload through a user-space buffer solely to hand it straight back to the kernel.
A broker can instead ask Linux to transfer data from a suitable file descriptor to a socket using sendfile(). That lets the kernel move the data without making the application own a duplicate payload buffer. When supported by the kernel, socket, filesystem and network path, the transfer can reuse page-cache-backed data efficiently. Zero-copy does not mean zero CPU, zero I/O, or that no copy is ever made anywhere in the stack.
The workload decides whether the optimization is useful
This helps most when the content is already in the correct wire representation. An append-only log segment containing records that consumers want unchanged is a good candidate. The producer writes a durable representation, and many consumers read the same range. Page cache may serve those repeated reads without disk activity, and the network stack can send the bytes without an extra application-level copy.
But suppose the broker must compress each batch differently for each consumer, transform a schema field, redact sensitive content, or encrypt at the application layer. Now the application needs to inspect or modify bytes. A straightforward sendfile() from the original file cannot provide that transformation. The system might preprocess immutable variants or accept a copy on the transformation path. Which choice wins depends on the frequency and cost of the transformation.
Transport encryption is another important boundary. Plain sendfile() does not magically turn file bytes into encrypted TLS records in user space. Some platforms support kernel TLS and other optimized paths that can preserve parts of the benefit, but they depend on configured support. For an arbitrary Java TLS socket, you must verify the actual stack before claiming a zero-copy path.
Measure where the cycles went
Before adopting zero-copy, I would profile CPU time, memory bandwidth, syscalls, socket queueing, page-cache hit rate and disk read activity. If the process spends most of its CPU in payload serialization rather than copying, sendfile() may not address the bottleneck. If the network interface is already saturated, eliminating copies may lower cost per byte without increasing total bytes per second.
A basic controlled test would hold log size, message size distribution, file-cache warmth, TLS mode and consumer concurrency constant. Compare a buffered read/write implementation with the optimized file-to-socket path. Measure throughput per core, user and system CPU, context-switch and syscall counts, tail latency, and the effect on other workloads sharing memory bandwidth.
There is another trap: sendfile() does not promise that one call transfers the entire requested byte range. Calls can transfer fewer bytes than requested, be interrupted, or report an error. Correct code must account for partial progress and retry from the right offset without duplicating or skipping bytes. Certain input/output descriptor combinations and filesystems are unsupported, so the application needs a safe fallback. When the socket path retains references to file-backed pages, those bytes must remain unchanged until the receiver has consumed them; immutable segments make that contract much easier to meet. That correctness handling matters more than a headline throughput number.
Why log brokers care about representation
The file format is part of the architectural choice. If a broker persists record batches in a representation consumers can read directly, it can reuse those bytes efficiently. If every read requires reconstructing an object graph, reserializing it and recompressing it, the storage format imposes recurring CPU cost even when the data never changes. Zero-copy is therefore tied to the wire/storage boundary, not just a clever system call.
This also affects cache behavior. Keeping useful log segments in the operating system page cache can be an effective shared caching strategy. A broker may avoid maintaining a separate large user-space cache for unchanged file bytes. But page cache competes with other memory demands, and a cold read still requires storage I/O. The lesson is not that page cache guarantees memory residency.
For the interview, I would make the boundaries explicit. Show which copies the data path actually eliminates, prove that the payload is unchanged, handle partial sends correctly, and test whether CPU copying is the active bottleneck. If those conditions don't hold, zero-copy can add implementation complexity without solving the user's problem.