There's one thing better than optimal deallocation: avoiding deallocation completely. This is where the concept of object pooling comes in. The idea behind object pooling is that we have a finite collection of reference types in memory, and instead of deallocating an object when we're done with it, we add it to the collection to be reused later.
With this method, new allocations only need to be made when there aren't any objects in the collection not being used. Even when new memory needs to be allocated, it goes into the pool to be reused when we're done with it, effectively increasing the capacity of the pool permanently and reducing the odds that we'll need to allocate again in the future.
The following diagram displays how this system works at a high level:
Let's implement a very basic object pool that we can use in all of our future projects to see this principle in action.