New Pooling API in Unity 2021

Dmitry Gordeev
WaveAccess
Published in
4 min readFeb 21, 2022

In games, in most cases, you are faced with lots of instantiations and disposes of objects. But when you reach some amount of instantiations, every second performance can degrade dramatically. To deal with it you have several options:

  • reduce the amount of instantiating objects per second
  • buy some pooling system in Asset Store
  • implement your own pooling system
  • use the new Unity Pooling API

Yes, Unity 2021+ has a great built-in pooling API and we are going to talk about it in this article.

Object Pool

The object pool is a well-known creational design pattern. The main idea is that the pool contains a list of already instantiated objects right after the start. When in a program you need a new instance of a specific object you do not directly instantiate it instead you ask the object pool for it and when you already do not need it you return it back to the object pool.

In the case of using an object pool, we have no instantiations and dispose of them all the time. It means no memory allocations so for the garbage collector much less work here (and no micro-freezes).

UnityEngine.Pool

The UnityEngine.Pool namespace contains several pools:

  • CollectionPool<T0,T1>
  • DictionaryPool<T0,T1>
  • GenericPool<T0>
  • HashSetPool<T0>
  • LinkedPool<T0>
  • ListPool<T0>
  • ObjectPool<T0>
  • UnsafeGenericPool<T0>

CollectionPool<T0,T1>

This is the base implementation of object pool for collections like Dictionary (DictionaryPool<T0,T1>), List (ListPool<T0>), HashSet (HashSetPool<T0>).

Base CollectionPool<T0,T1> and derived classes have the same static API.

  1. public static TCollection Get();
  2. public static PooledObject<TCollection> Get(out TCollection value);

As an example let’s assume that you need to generate geometry at runtime, so you often need a few collections like List<Vector3> to store generated vertices and you need to run generation periodically. In that case, you can use ListPool<T0>:

Example of using ListPool

As you can see, using ListPool<T0> is very simple:

  1. Get a list from the pool
  2. Use the list as you want
  3. Return the list back to the pool

The pool clears the list when it is returned back to the pool.

This approach is good if you need to keep a list for a while but in the case when you need a list in one place in one frame CollectionPool has a shorter way:

Example of using ListPool with using operator

When the pooled object leaves the scope it will be Disposed of and returned to the pool automatically.

Usage of CollectionPool is easy but you don’t have control over how an object is created, initialized, and deinitialized.

ObjectPool<T0>

The ObjectPool<T0> was created to allow you to have full control of how objects are created, initialized, and deinitialized.

In many cases, you need a pool for GameObjects so the example below will be for GameObject.

To create an instance of ObjectPool the first thing you need is to define how to create an object which will be in the pool.

You can add components, add children GameObjects, instantiate prefabs, and so on.

Then you need to define what happens when an object will be taken from the pool.

Then you need to define what to do when the object returns to the pool. Usually, just deactivate an object.

In some cases that will be described a little bit later, objects need to be destroyed. So you need to define how to destroy.

Ok, by now we have defined all the necessary methods that the pool needs. So let’s create the pool instance!

You may notice the pool constructor needs three more parameters:

  • collectionChecks —collection checks are performed when an instance is returned back to the pool. An exception will be thrown if the instance is already in the pool. Collection checks are only performed in the Editor.
  • defaultCapacity — the default capacity the stack will be created with.
  • maxPoolSize — the maximum size of the pool, if you request more objects from the pool it will create a new instance for you but when you return it to the pool it will be destroyed. That’s why we need the method that we defined later OnDestroyPoolObject.

Now you can use the pool instance as the previous ones. The instance has the same two methods to retrieve an object from the pool:

  • public T Get();
  • public PooledObject<T> Get(out T v);

and one that allows you to return an object to the pool:

  • public void Release(T element);

Also, there exist three more pool implementations:

  • LinkedPool<T0> — the difference between ObjectPool and LinkedPool is that ObjectPool uses the stack for storing objects and LinkedPool uses a list
  • GenericPool<T0> — static implementation of ObjectPool
  • UnsafeGenericPool<T0> — the same as GenericPool but without collection check.

Conclusion

As you can see the new Pool API in Unity 2021 is easy to use and allows you to have full control of how and what is done at every step.

I hope this article will allow you to make what you want before you have any performance issues caused by objects instantiations and GC work and let you implement your next great ideas

--

--