Object pooling in dart improves the memory performance of your Flutter app or game.
When a large number of objects (such as bullets or enemies) are created over time in a Flutter game, dart has to do a lot of work allocating and then garbage collecting that memory.
The garbage collector (GC) does not run continuously, so memory usage can increase dramatically between sweeps, resulting in high spikes of memory usage. Read more about dart’s garbage collection.
Object pooling can help prevent fragmentation and reduce those peaks by re-using objects from a pool instead of creating and destroying them every time. This is especially useful on lower-memory devices such as smart watches or cheaper smart phones.
Implementation of object pooling
Here is a simple implementation of object pooling in dart. At it’s core, the pool is simply a list of the same type of object.
// An object that is pooled should mix the Pooled mixin
mixin Pooled {
// Implement this to reset the object ready for re-use
void reset();
}
// Object pool implementation
class Pool<T extends Pooled> {
// A list that holds the pooled objects
final List<T> _pool = <T>[];
// The method called to create a new pooled object
final T Function() _creator;
// Constructor
Pool(this._creator);
// Get an object from the pool, or create one if pool is empty
T get() {
if (_pool.isEmpty) {
return _creator();
} else {
T obj = _pool.removeLast();
obj.reset();
return obj;
}
}
// Add an object (back) to the pool
void add(T obj) {
_pool.add(obj);
}
// Clear the pool and release all the pooled objects for GC
void clear() {
_pool.clear();
}
}
DartTo use it, create a new pool for every different object type that you want to pool. Bullets are a good choice as they are often created and destroyed in large numbers.
// Mix the Pooled mixin with the class that should be pooled
class Bullet with Pooled {
// Create a static pool, passing in the constructor of the object
static Pool<Bullet> pool = Pool<Bullet>(Bullet.new);
// These are some example properties on Bullet
double damage = 1.0;
double speed = 20.0;
// Set the Bullet back to default values when re-used
@override
void reset() {
damage = 1.0;
speed = 20.0;
}
}
DartThen, whenever you need a new bullet get it from pool. Also remember to add it back to the pool once it is no longer needed.
// Instead of this: Bullet b = Bullet();
Bullet b = Bullet.pool.get();
// Once the bullet is no longer required
Bullet.pool.add(b);
// When the game is over, free any pooled objects
Bullet.pool.clear();
DartHere is the result – these are screengrabs of the memory profiler without and with object pooling enabled.
Without object pooling, as many as 1000 bullets can remain in memory before the garbage collector sweep removed them.

With object pooling enabled there are a maximum of 20 bullets in memory. Instead of creating new objects, recycled bullets are re-used from the pool.
