Pool try to get destroyed object

I’m using UnityEngine.Pool in my project, but I ran into an issue.

When I set a low maxCapacity, if I take out more objects and then return them to the pool, the extra objects (beyond the capacity) are destroyed as expected. However, on subsequent calls to Get(), it looks like the pool is still (accidentally) trying to reuse those destroyed objects, which results in null.

Apart from simply increasing the maxCapacity, is there a recommended way to avoid this behavior?

using UnityEngine.Pool;

namespace Game.Controller
{
    public abstract class SystemPool<T> : IObjectPool<T> where T : PoolingObject
    {
        #region ----- Component Config -----

        // Collection checks will throw errors if we try to release an item that is already in the pool.
        protected bool _collectionChecks = true;
        protected int _maxPoolSize = 10;

        private IObjectPool<T> _pool;
        public int CountInactive { get; }

        #endregion

        #region ----- Constructor -----

        public SystemPool(){}

        #endregion
        
        #region ----- Property -----

        protected IObjectPool<T> Pool
        {
            get
            {
                if (_pool == null)
                {
                    _pool = new ObjectPool<T>(CreatePooledItem, _OnTakeFromPool, _OnReturnedToPool,
                        _OnDestroyPoolObject, _collectionChecks, 10, _maxPoolSize);
                }

                return _pool;
            }
        }

        #endregion

        #region ----- Private Function -----


        protected abstract T CreatePooledItem();

        // Called when an item is returned to the pool using Release
        private void _OnReturnedToPool(T obj)
        {
            obj.OnRelease();
            obj.gameObject.SetActive(false);
        }

        // Called when an item is taken from the pool using Get
        private void _OnTakeFromPool(T obj)
        {
            obj.OnGet();
            obj.gameObject.SetActive(true);
        }

        // If the pool capacity is reached then any items returned will be destroyed.
        // We can control what the destroy behavior does, here we destroy the GameObject.
        private void _OnDestroyPoolObject(T obj)
        {
            obj.OnRelease();
            // UnityEngine.Object.Destroy(obj.gameObject);
        }

        #endregion

        #region ----- Implement Function -----

        public T Get()
        {
            return Pool.Get();
        }

        public PooledObject<T> Get(out T v)
        {
            return Pool.Get(out v);
        }

        public void Release(T element)
        {
            lock (Pool)
            {
                if (element.IsRelease())
                {
                    return;
                }
                element.Release();
                element.gameObject.SetActive(false);
                Pool.Release(element);
            }
        }

        public void Clear()
        {
            Pool.Clear();
        }


        #endregion
    }
}

That sounds like a bug in the pooling code. If it’s not your code then review the API because perhaps you misused it.

The answer is always the same… ALWAYS!

How to fix a NullReferenceException error

Three steps to success:

  • Identify what is null ← any other action taken before this step is WASTED TIME
  • Identify why it is null
  • Fix that

NullReference is the single most common error while programming. Fixing it is always the same.

Some notes on how to fix a NullReferenceException error in Unity3D:

IF YOU STILL WANT TO USE POOLING:

The costs and issues associated with object pooling / pools:

In very rare extremely-high-count object circumstances I have seen small benefits from pooling.

In MOST circumstances, object pooling is a source of complexity, bugs and edge cases while not giving any measurable benefit.

Thank you for your feedback.

I’ve checked the code. When returning the object to the pool, I call SetActive(false), and this line still works, which means I’m not returning a null object to the pool.

The null exception is thrown at line 57, when calling .Get(), so I think the issue might be inside the pool itself.

Can you confirm whether the pool implementation actually adds destroyed objects back into the pool? From your code, I don’t see any call to _pool.CountInactive within your own CountInactive implementation. After destroying any extra objects, check the value of CountInactive. If it doesn’t increase, then those objects are being destroyed outright rather than returned as null to the stack.

It’s also difficult to be certain without seeing the complete code, but are you sure that all derived classes of your SystemPool implement CreatePooledItem so that it returns a new instance of T?

Lastly, what’s the purpose of having an empty public constructor in an abstract class? The only real reason to include one is to maintain backward compatibility in a library or framework, in case you later need to add a constructor with parameters. But in the case of an abstract class, this doesn’t make sense.

It’s very easy to fall into the trap of thinking: “I’m sure my code is correct, so it must be someone else’s fault that it doesn’t work.” But in 99.9% of cases, especially with popular, widely used libraries, the mistake is our own.

I’ve identified the root cause of the issue.
UnityEngine.Pool relies on the CountInactive property internally for its calculations and object handling. In my implementation, I overlooked providing this, which caused the unexpected behavior.

After updating my code to properly implement CountInactive, the pool now works as expected.