Real-time simulations and games spawn and remove huge numbers of short-lived objects —
bullets, particles, enemies, floating combat text. The naive approach calls
new (allocate geometry, material, buffers) every time an object appears and
throws it away when it disappears, leaving the job of reclaiming that memory to the
garbage collector. The object pool pattern instead pre-allocates a fixed
set of objects once, and "spawning" just means pulling an idle one out of the pool and
resetting its state — no allocation, no garbage, ever.
performance.now().Instantiate/Destroy against a pooling system.
This is also why data-driven engines pair pooling with something like Unity's
ScriptableObject — shared, read-only configuration data (colors, speeds,
stats) lives once in memory and is referenced by every pooled instance instead of being
copied and re-copied on every spawn.
A split-screen 3D scene where identical projectiles spawn on both sides at the same rate — the left half allocates and disposes a fresh mesh for every spawn, the right half recycles instances from a fixed pool, and a live graph plots the real per-frame cost of each approach.
The naive side genuinely creates new geometry and material objects and disposes them on despawn, producing measurable, spiky per-frame update cost. The pooled side reuses a pre-built array of meshes with no runtime allocation, staying low and flat even as spawn rate climbs.
Raise the spawn rate and GC pressure sliders to stress both scenes, then watch the frame-time graph split apart. Adjust pool capacity to see what happens when the pool runs out of idle instances.
Game engines like Unity and Unreal ship built-in pooling APIs precisely because frequent small allocations — not big ones — are what trigger frame-hitching garbage-collection pauses in real-time loops.