Why does a linked list of 10 million 8-byte readings dwarf a flat buffer in memory?
answer
- Count everything the record carries
- The value is not the only field
- Every separate allocation has bookkeeping
- Round each block to a size class
- Compare 8 useful bytes to 32 spent
basics
~20 sEach reading becomes its own heap record: the 8-byte value, a link field of one machine word, and the allocator's per-block header and size rounding. That is commonly 24 to 32 bytes per element instead of 8 — a three- to fourfold blow-up, not a saving.
solid answer
~50 sThe claim that a chained structure "allocates only what it needs" prices the payload and forgets the wrapper. On a 64-bit machine each node carries the 8-byte reading plus a link field of another 8 bytes, and every separate allocation typically costs a header of one or two words and gets rounded up to the allocator's next size class. So a node holding 8 useful bytes commonly occupies 24-32 bytes: 10 million readings land somewhere near 240-320 MB against 80 MB for one flat buffer. A growth-doubling contiguous buffer wastes capacity too, but its worst case is roughly 50% slack, not 300%. The overhead is also per element, so it grows exactly where it hurts — at scale, with small payloads. Chained nodes are worth their price for cheap local restructuring and stable addresses, never for compactness.
go deeper
Know that a node is not just the value: it also holds a reference to the next node, and every separately allocated record carries some bookkeeping. That alone explains why the total exceeds the sum of the data.
Do the arithmetic out loud — payload, link field, allocation header, size rounding — and land on a ratio for a small payload. Explain why the overhead is per element and therefore never amortizes away.
Turn the audit into a decision: name the payload size where the wrapper stops mattering, and offer the structural fixes (block-per-node, pool allocation, inline payloads) rather than accepting the blow-up as inherent.
Own the framing for the team: memory density is one axis and cheap local restructuring is another, and a fleet-wide memory ceiling decides which one wins. Be able to justify paying 3x memory when the edit pattern genuinely requires stable addresses.
## The claim under audit "Linked structures save memory because they allocate only what they need." It sounds right — no reserved capacity, no empty slots, one allocation per element that arrives and one freed per element that leaves. It is wrong for small payloads, and the audit that proves it is arithmetic anyone can do at a whiteboard. ## Costing one node Take ten million sensor readings, eight bytes each. Stored in one flat buffer, that is 80 MB and essentially nothing else: the elements are the storage. Stored as a chain, each reading becomes an independent heap record. Count what that record costs on a typical 64-bit machine: | Component | Typical size | | --- | --- | | The reading itself | 8 bytes | | Link field (reference to the next node) | 8 bytes | | Per-allocation header / bookkeeping | 8-16 bytes | | Alignment and size-class rounding | 0-8 bytes | | **Total per element** | **24-32 bytes** | Three to four times the payload — and the payload is the only part that carries information. Ten million readings become roughly 240-320 MB. Every extra link field a design adds is another word per node on top of that, and if the node stores a *reference* to the reading rather than the reading inline, you pay a second allocation and a second header for the value itself. Two secondary costs usually go unmentioned in the interview and are worth raising: - **Allocator bookkeeping beyond the header.** Millions of small live blocks give an allocator its worst case: metadata, free-list structure, and internal fragmentation across size classes. - **Nothing is amortized away.** The overhead is strictly per element. Doubling the element count doubles the wasted bytes; there is no fixed cost to spread. ## The honest comparison Contiguous storage is not free of waste. A buffer that grows by doubling carries unused capacity between growth events — in the worst case just after a growth, roughly half the block is slack, and the amortized average is smaller. So the fair statement is: a contiguous buffer wastes up to about 50% at a bad moment, and can be trimmed to exact size when it stops growing; a chain of small nodes wastes 200-300% permanently, and cannot be trimmed because the waste is structural. The balance shifts with payload size. If each element is a 400-byte record, a 24-byte wrapper is 6% overhead and nobody cares. The blow-up is a small-payload phenomenon, which is exactly why "eight-byte readings" is the shape that exposes it. The other thing that shifts the balance is *churn*: if half the elements are removed, a chain returns their memory immediately, while a contiguous buffer keeps its capacity until something explicitly shrinks it. ## Where the fixes actually live When the audit says the wrapper is the problem, the standard moves are structural, not micro-optimizations: - **Store more per node.** Give each node a small block of readings instead of one, so the header and link amortize over dozens of values. This is the classic unrolled-chain compromise and it recovers most of the density while keeping cheap splicing at block granularity. - **Allocate nodes from a pool.** Carving nodes out of one large region removes most of the per-allocation header and rounding cost, at the price of managing the region's lifetime. - **Keep the payload inline.** A node holding the value directly costs one allocation; a node holding a reference to a separately allocated value costs two, with two headers and an extra hop on every read. ## The interview move When a candidate asserts the memory saving, the interviewer is waiting for one number: the ratio of wrapper bytes to payload bytes. Producing "about 24-32 bytes for 8 bytes of data, so three to four times" — with the caveat that it depends on the machine's word size and the allocator's rounding — is the answer that ends the discussion. Refusing to give any number, or giving one with false precision as though it were universal, are the two ways to lose it. Mainstream runtimes differ here in ways worth knowing: managed environments typically attach an object header of one to two words to every heap record for type and bookkeeping purposes, while an unmanaged allocator adds its own block header instead — the header is smaller in some setups and larger in others, but no mainstream environment gives you a per-element allocation for free. The magnitude of the multiplier moves; the direction never does.
- At what payload size does the per-node overhead stop mattering?Roughly when the payload dwarfs the wrapper. At 8 bytes the wrapper is 200-300%; at a few hundred bytes per element it is a low single-digit percentage and no longer part of the decision. That is why the memory argument against chained nodes is really an argument about small elements at high counts, and should be restated that way rather than as a blanket rule.
- Is there a case where the chain genuinely uses less memory than a contiguous buffer?Yes, under heavy shrinkage. A chain returns a node's memory the moment the element is removed, while a growth-doubling buffer keeps its peak capacity until something explicitly trims it. If a structure spikes to ten million elements and settles at ten thousand, the untrimmed buffer holds the peak and the chain does not.
- How would you keep the splicing behaviour but cut the memory blow-up?Store a small block of elements per node instead of one. The header and link field then amortize over dozens of values, recovering most of the density, while splicing still works at block granularity. The cost is that edits inside a block shift elements within it, so the O(1) edit guarantee weakens to O(block size).
Shipping ten million grapes individually boxed and labelled uses far more cardboard than grape, however tightly each box fits its grape.
saying these in an interview costs you the question
- Says linked structures save memory by avoiding spare capacity
- Counts only the payload, ignoring link and header
- Thinks per-node overhead amortizes away at scale
- Believes a doubling buffer wastes more than 3x
- Quotes one exact byte count as universal