Process

Object Pooling in Unity: The Pattern That Fixes Most Mobile Stutter

A stutter every few seconds is usually the garbage collector, not the renderer. Object pooling is the pattern that removes it, and the mistakes that make it useless are avoidable.

Vectra Play 6 min read
A laptop computer open on a desk

Object pooling reuses a fixed set of objects instead of creating and destroying them at runtime. It removes the allocation and cleanup that causes periodic stutter on mobile. Pool anything that appears more than a few times per second: projectiles, particles, pickups, damage numbers, and list rows.

Why this matters

A game can hold a steady frame rate and still feel broken. Players notice a hitch far more than they notice a consistently lower frame rate, because a hitch arrives in the middle of an input.

On mobile, those hitches usually come from memory rather than rendering. Creating objects allocates, allocation eventually triggers collection, and collection pauses the frame.

Pooling is the standard fix, it is well understood, and it is one of the few optimisations that reliably pays for itself in a day.

Why Instantiate and Destroy cause stutter

Two costs sit behind those calls, and only one of them is obvious.

The visible cost is the work itself. Instantiate copies a prefab, allocates the objects behind it, wires up components, and runs their startup code. Destroy tears that down.

The hidden cost is what happens later. Destroyed objects leave managed memory behind, and at some unpredictable point the collector runs to reclaim it. That pause is the stutter, and it arrives long after the code that caused it.

The frame that stutters is rarely the frame that caused the problem. That is why profiling by eye leads people to the wrong fix.

What pooling actually does

A pool creates the objects once, keeps them alive, and hands them out.

The lifecycle changes from create and destroy to activate and deactivate.

  1. At startup, create a fixed number of instances and disable them.
  2. When something is needed, take an inactive one, reset it, and enable it.
  3. When it is finished, reset anything stateful and disable it again.
  4. Nothing is ever destroyed, so nothing needs collecting.

The frame cost becomes small and predictable. The memory cost becomes fixed and known, which on mobile is worth as much as the speed.

A minimal pool implementation

The pattern is small enough to describe without code.

A pool needs four things: a prefab to copy, a container to keep instances in, a stack of inactive instances, and a rule for what happens when the stack is empty.

That last rule is the design decision that matters.

Empty-pool policyBehaviourWhen to use it
GrowCreate another instanceSafe default while tuning
Recycle oldestReuse the longest-lived active objectEffects and particles
Fail quietlyReturn nothing, skip the spawnNon-essential visuals

Growing is the right starting point because it never breaks the game. Once you know the real peak, switch to a fixed size and recycle, so memory stops moving.

The other essential detail is the reset. Pooled objects keep their state, so anything the object changed while active must be put back: position, rotation, velocity, colour, animation state, coroutines, and any timers.

Most pooling bugs are missed resets rather than pooling itself.

Unity's built-in ObjectPool versus rolling your own

Unity ships a generic pool that covers the common case, with callbacks for creating, taking, returning, and destroying an item.

Use it when your needs are ordinary. It is tested, it is readable, and it removes a class of mistakes.

Write your own when you need something it does not offer.

Pre-warming deserves attention. Creating four hundred instances in one frame at startup produces exactly the hitch you were trying to remove, moved to the loading screen. Spread it, or do it behind a transition.

Pooling particles, audio, and UI

The three highest-value targets are frequently missed.

The UI case has a second benefit. Fewer active elements means smaller canvas rebuilds, which is covered in Unity UI That Does Not Tank Your Frame Rate.

The mistakes that make pooling slower

Pooling done carelessly can cost more than it saves.

  1. Pools that only grow. Without a ceiling, a spike allocates hundreds of objects that then sit in memory forever.
  2. Missed resets. Objects come back with old velocity, old colour, or a coroutine still running, producing bugs that look random.
  3. Searching for a free object. Iterating a list to find an inactive instance is linear work every spawn. Use a stack or queue.
  4. Pooling things that are rare. A boss that spawns twice per session does not need a pool, and pooling it adds complexity for nothing.
  5. Leaving pooled objects enabled. Disabled objects still cost a little; enabled ones cost the full update and render.
  6. Pooling without measuring. If allocation was not the bottleneck, this changes nothing and you spent the week anyway.

That last point is the important one. Profile first, as described in Unity Mobile Performance.

What we would do

Pool from the start for the obvious categories: projectiles, effects, pickups, floating text, and list rows. Retrofitting is more work than doing it once at the beginning, and these categories are predictable.

Set pools to grow during development so nothing breaks, then log the peak. Before shipping, fix each pool at its observed peak plus a small margin and switch to recycling.

Write the reset alongside the object, not later. A component that knows how to return itself to a clean state is the piece that makes pooling maintainable.

The short version

Profile a session on a real device and look for periodic spikes. If you see them, start with projectiles and effects. If you would like help finding them, send us the build.

Related reading: Unity Mobile Performance, Fixing Physics Jitter in Unity Mobile Games, and Save Systems That Do Not Corrupt.

#Unity#Mobile#Process#Development
Your turn

Got a game idea? We build it.

You bring the concept. We design, build, test and launch it, and you own 100% of the finished game.

Share Your Game Idea  →