In a per-request scratch arena reset at the response boundary, how do allocation and release actually work?
answer
- one cursor, one direction
- no header beside each object
- release is not per object
- the boundary is the release point
- rewinding the cursor releases everything
basics
~20 sAllocation aligns one cursor, hands back its old position and advances it past the object, storing no per-object metadata. Nothing is released individually: at the response boundary the cursor is rewound to the start, which releases everything in the region at once.
solid answer
~50 sAn arena is one contiguous region with a single cursor, often called a bump pointer, dividing memory already handed out from memory still free. To allocate, you round the cursor up to the required alignment, check the object still fits before the end of the region, return the rounded cursor and set it past the object. There is no search, no size class and no header beside the block, because the allocator will never be asked about a single object again. Release is not per object at all: at the phase boundary you `reset` the arena by rewinding the cursor to the start, which releases every object in it in constant time regardless of how many there were. The price is that nothing comes back early, and any pointer that survives the reset now addresses storage the arena will hand out again.
code
pseudocode · 10 linesfunction arena_alloc(arena, size, alignment):
p = round_up(arena.next, alignment)
if p + size > arena.end:
return grow_or_fail(arena, size) # chain a block, or fail the phase
arena.next = p + size
return p # no header, no free list entry
function arena_reset(arena):
arena.next = arena.start # every object released at once
# bytes are NOT cleared and pages are NOT returned herego deeper
Remember the shape: one region, one cursor that only moves forward, and a single reset that releases everything when the phase ends.
Be able to walk the allocation path out loud — align, bounds-check, return, advance — and say why no per-object header exists and why that makes individual release impossible.
Show that you treat the reset as a contract: nothing may outlive it, non-memory resources still need explicit cleanup, and the per-phase allocation total is a budget somebody owns.
Frame it as moving release cost to a boundary the application already has, and be ready to say which workloads have no such boundary and therefore should not pay the design's obligations.
## What an arena is An **arena**, also called a **region**, is a contiguous block of memory that a program obtains once and then sub-allocates itself. Inside it sits a single cursor — the **bump pointer** — marking the boundary between the part of the region already handed out and the part still free. Every arena has an owner and a lifetime, and in the shape this question is about, the lifetime is a **phase**: one request, one rendered report, one parse, one frame. The defining property is not speed. It is that **the release decision is made once, for the whole region, by whoever owns the phase** — not object by object by whoever happened to allocate. ## The allocation path Allocating from a bump-pointer arena is three steps and no search: 1. Round the cursor up to the alignment the requested object needs. 2. Check that the rounded cursor plus the requested size still lies before the end of the region. 3. Return the rounded cursor as the address, and set the cursor to just past the new object. What is absent matters more than what is present. There is no free list to walk, no fitting decision, and no per-object header recording a size so that a later release can find the block. The allocator keeps **no per-object metadata at all**, because nothing will ever ask it about one object again. Two consequences follow: the fast path is a handful of instructions with no branch that depends on the heap's history, and objects allocated in sequence end up adjacent in memory, which a walk over them later reads out of cache in order. If step 2 fails, an arena has two honest policies: **chain another block** (the arena becomes a list of blocks, and contiguity now holds per block rather than across the whole arena), or **fail the phase** against a declared budget. Choosing deliberately between those two is part of adopting the design. ## The release path Release is the reset: the cursor goes back to the start of the region. Every object in it dies at the same instant, and the cost is constant — one store — no matter whether the phase allocated ten objects or ten million. That is the whole trade: the per-object release work that a general-purpose allocator does N times is replaced by one operation at a boundary the application already has. Three things a reset does **not** do, and each is a real source of bugs: - It does not **zero** the region. The bytes of the previous phase are still there until something writes over them. - It does not necessarily hand the memory back to the operating system. The usual behaviour is to keep the backing pages so the next phase reuses warm memory; returning pages is a separate, explicit decision. - It does not run any **per-object cleanup**. An object that owns something other than memory — an open handle, a lock, a registration in some table — must have that released explicitly. The arena reclaims bytes and nothing else. ## Against a general-purpose allocator | | Bump-pointer arena | General-purpose allocator | |---|---|---| | Allocate | Advance one aligned cursor | Find a block that fits the request | | Per-object metadata | None | A header or tag beside each block | | Release one object | Not possible | Supported, and expected | | Release everything | One cursor reset | One release per live object | | Peak footprint | Everything allocated in the phase | Roughly the live set | | Dominant risk | A pointer outliving the reset | Forgetting an individual release | ## What the design buys, and what it obliges It buys a fast, branch-light allocation path; constant-time bulk release; no per-object bookkeeping; good locality; and the disappearance of one whole bug class, the object nobody remembered to release. It obliges you to accept that **nothing is reclaimed early**. Memory that goes unused halfway through the phase is still held until the boundary. It obliges you to copy out, before the reset, anything that must outlive the phase — otherwise the caller keeps a pointer into storage the arena is about to hand out again, and reads there return whatever the next phase wrote. And it turns the per-phase allocation total into a number somebody now owns, because it is the number that decides the process footprint. The design therefore fits work with a **clear boundary and a bounded budget**: build the whole report, write the response, reset. It fits badly where objects have individual, unpredictable lifetimes, or where the phase can run arbitrarily long.
- What has to happen to an object that must outlive the phase the arena serves?It has to be copied into storage with a longer lifetime before the reset, and the caller must be handed the copy. Keeping the original pointer is unsafe: after the reset the arena hands that same storage out again, so later reads see the next phase's data rather than the object.
- Does resetting an arena give the memory back to the operating system?Not by default. A reset rewinds the cursor and normally keeps the backing pages, precisely so the next phase allocates into warm memory with no system call. Returning pages is a separate, explicit action, and it costs the next phase the faults it avoided.
A whiteboard used for one meeting: you write wherever the last line ended and never erase a single word, then wipe the whole board when the meeting ends. Anything you still need has to be photographed before the wipe.
saying these in an interview costs you the question
- Thinks an individual object inside an arena can be released on its own.
- Assumes a reset zeroes the region, so recycled bytes are always clean.
- Believes a reset also runs cleanup for handles and locks the objects held.
- Says an arena removes the need for any memory budget on the phase.
- Claims bump allocation makes the whole process free of fragmentation.
- Keeps returning pointers into the arena to callers that outlive the phase.