core24 min studyLesson 129 in Engineering

Arenas and pools: reuse storage without reusing stale meaning

Compare bulk-lifetime arenas with reusable slots while accounting for alignment, cleanup, retention, and stale handles.

On this page

What you’ll understand

  • Calculate aligned bump-allocation offsets within a bounded arena.
  • Choose bulk reset only when every dependent access has ended.
  • Explain pool retention and the stale-handle risk introduced by slot reuse.

An allocator strategy should match the lifetimes of the objects it stores. A request-local arena can make many small allocations cheap to reclaim together; a pool can repeatedly reuse fixed-size slots. Neither strategy removes ownership obligations. Bulk reset and slot reuse are precisely the moments when an old pointer or handle can continue to exist while the meaning of its storage has changed.

An arena groups a lifetime decision

A simple bump arena reserves a region and advances a cursor for each allocation, inserting alignment padding as needed. It can reclaim all allocations together by resetting the cursor after every user of those objects has finished. This fits a parser whose temporary syntax nodes all die after one request. It fits poorly when one tiny result must escape and keep an entire large region available. Copying an independent result out before reset may be a better boundary than allowing a reference to escape.

Bulk storage reclamation and resource destruction are different operations. Objects can own files, locks, external buffers, or reference-counted handles that need cleanup even if their containing bytes are cheap to discard. An arena API may track destructors, require explicit cleanup, or restrict the types it stores. Read that contract. Never assume resetting raw storage automatically performs every object's semantic cleanup. Also distinguish returning the cursor to the start from releasing the arena's backing reservation or physical pages to the operating system.

paligned=a⌈pa⌉,pnext=paligned+s,pnext≤Cp_{\rm aligned}=a\left\lceil\frac{p}{a}\right\rceil,\qquad p_{\rm next}=p_{\rm aligned}+s,\qquad p_{\rm next}\leq C
p and C are byte offsets and capacity, s is the positive object size in bytes, and a is its positive required alignment in bytes. This relative-offset formula assumes the arena base is suitably aligned; real implementation must check rounding, addition overflow, layout limits, and construction validity.↔ Scroll wide equations horizontally.

A pool trades repeated allocation for retained slots

A fixed-size pool divides backing storage into reusable slots and tracks which slots are available. A released slot can later hold a different object, so an old pointer cannot be validated by its address alone. Handle-based designs sometimes attach a generation number to the slot index and compare it on lookup. That helps detect stale handles within the design's assumptions, but finite generation counters can wrap and concurrent reclamation still needs a synchronization argument. A pool's bookkeeping is part of its safety boundary, not merely a performance detail.

Pools can retain a workload's high-water capacity after demand falls. Per-thread caches can multiply this retained capacity, while sharing one pool can introduce contention. Size classes reduce external searching but can waste space inside each slot when object sizes differ. Compare the total retained backing storage, metadata, alignment padding, and actual useful live payload. Reuse can also preserve sensitive bytes unless initialization or clearing handles them appropriately. A faster allocation path is not sufficient evidence that the whole request uses less memory or less CPU.

Mslots=nS,Museful=ℓs,U=ℓsnSM_{\rm slots}=nS,\qquad M_{\rm useful}=\ell s,\qquad U=\frac{\ell s}{nS}
n retained slots have S bytes per slot, while ℓ live objects each use s payload bytes, with 0≤ℓ≤n and 0<s≤S. U is a dimensionless useful-payload fraction, excluding out-of-slot metadata and shared backing overhead. Free retained slots still contribute to M_slots.↔ Scroll wide equations horizontally.

Measure the lifecycle the allocator serves

Before introducing an arena or pool, profile allocation count, lifetime distribution, peak simultaneous demand, and time spent in the existing allocator. A request arena can reduce bookkeeping yet keep dead intermediates until request completion, increasing peak live backing storage relative to earlier individual releases. A pool can avoid repeated native calls but add cache misses or synchronization. Compare complete workloads, including cancellation and large requests, rather than timing a repeated allocate-free loop whose working set stays unrealistically small.

The function lab illustrates a specific payload that an arena cannot make disappear. Let positive x be a normalized batch dimension and a the normalized bytes per entry of a dense square table. With a=1, increasing x from one to two raises y=ax² from one to four. Both values are reachable with the actual controls. The lab does not allocate an arena, align objects, or model fragmentation. It shows why grouping allocations by lifetime does not change an algorithm's quadratic payload requirement. After exploring, identify whether changing the representation or shortening retention would reduce that requirement.

Place three objects in an aligned arena

A 64-byte teaching arena has a base aligned to eight bytes, starts with cursor zero, and stores objects of 13, 20, and 7 bytes in that order. Each object requires eight-byte starting alignment. Ignore metadata.

  1. The first object starts at offset 0 and ends at 13. Round the next start up to 16, inserting three bytes of padding; the second object ends at 36.

  2. Round 36 up to 40, inserting four more padding bytes. The third object ends at 47, so the three payloads total 40 bytes and inter-object padding totals seven.

  3. The cursor is 47, leaving 17 bytes before the capacity boundary. Reset only after every access to all three objects has ended and their required cleanup has been performed according to the arena API.

The result

The layout fits, consuming 47 cursor bytes for 40 payload bytes. Bulk reset is a lifetime operation, not proof that escaped references or external resources are safe.

Explore the model

Interactive lab

Interactive lab · function

An arena does not change payload growth

Set a=1 in y=ax² and compare positive x=1 with x=2. The output grows from 1 to 4, representing a normalized dense square table's payload. The controls do not implement allocation, alignment, pooling, or reset; they expose an algorithmic memory requirement that those mechanisms cannot remove.

An arena does not change payload growthThis lab uses only y = ax². Playback traces the curve within the fixed integration interval from 0 to x; it does not estimate numerical error or change the chosen point. Current readings: y at chosen x: 0.608; Tangent slope · 2ax: 1.35; Signed area · ax³/3: 0.182. All parameters are adjustable with the labeled controls below.−2.4−1.201.22.4−9−4.504.59xy(x, ax²)
y = ax²TangentSigned integral
y at chosen x
0.608
Tangent slope · 2ax
1.35
Signed area · ax³/3
0.182
t = 0 s

This lab uses only y = ax². Playback traces the curve within the fixed integration interval from 0 to x; it does not estimate numerical error or change the chosen point. Parameter changes pause playback and recompute the model at the current elapsed time. Reset restores both parameters and returns time to zero. Playback starts paused. Adjust either parameter, then choose Play or Step.

Look forAllocator reuse changes storage management costs while the chosen representation still determines required payload.

Check your understanding

When is resetting a request arena justified?

Try it yourself

A pool retains 1,000 slots of 64 bytes. There are 250 live objects, each with 48 useful payload bytes. Calculate retained slot bytes, useful bytes, and the useful-payload fraction.

Take a moment to make a prediction or work it out on paper. Open a hint when you need one.

Give me a hint

Count all retained slots for storage and only live objects for useful payload.

Compare with the solution

Retained slot storage is 1,000 × 64 = 64,000 bytes. Useful live payload is 250 × 48 = 12,000 bytes. The fraction is 12,000/64,000=0.1875, or 18.75%, before additional metadata. A low fraction can reflect spare capacity, per-slot slack, or both; it does not by itself prove the pool is the measured bottleneck.

Take it with you

  • Arenas are effective when many objects genuinely share a reclamation boundary.
  • Resetting bytes and cleaning up owned resources are separate responsibilities.
  • Pools reuse slots but can retain capacity and expose stale-handle hazards.

Read further

Original sources and deeper explanations to follow at your own pace.

Optional progress, saved in this browser when available. Revisit any lesson at any time.

Lesson not marked complete.