Object Pool: Creational Design Pattern in C#

Smita Nangare
Globant
Published in
7 min readJul 12, 2023
Photo by Frank Okay on Unsplash

When the cost of initializing a class instance is very high, an object pool creational design pattern is used in this situation. In this article, we can see how an Object Pool Pattern can offer a significant performance boost and how it is most effective when the rate of instantiation of a class is high, and the number of instantiations in use at any one time is low.

The Object Pool Pattern

The object pool pattern is used to boost the application’s performance. Object Pool uses the mechanisms like garbage collection and reduced runtime memory allocation which helps to improve performance.

Below is a real-time example that describes this pattern.

If we consider an example of database connections, then opening too many connections might affect performance for some reasons. Creating a connection is expensive and the database server will become overloaded when too many connections are opened and it takes longer to open a new one. So, in such cases, the object pool manages the connections and provides a way to reuse and share them. It can also limit the maximum number of objects that can be created. Below is the diagrammatical representation of this scenario for database connections.

In Used and allocated DB connections

Object Pool in UML

Below is the UML representation of the Object Pool pattern.

Image courtesy from geeksforgeeks.org

As per the above UML, the terms used in the Object Pool pattern are below.

  • Client: This class uses the objects from the PooledObject type.
  • ReusablePool: The PooledObject class is the type that is expensive or slow to instantiate, or that has limited availability, so is to be held in the object pool.
  • ObjectPool: ObjectPool maintains a list of available objects and a collection of objects that have already been requested from the pool.

An Object pool is a container that contains some objects. So, when an object is taken from the pool, it is not available in the pool until it is put back. The object pool has a lifecycle phase including creation, validation, and destruction.

Object Pool Lifecycle Phases
  • Creation: Creating the pool with no object.
  • Validation: Checking if objects are available to use, create new, or wait.
  • Destruction: Destroying the pool when not needed.

An example in C#

To illustrate object pooling, we will create an Employee class to reuse the same objects.

Create the object pool

In the following example, we’ll create a custom objectPool class with the methods getObj() and releaseObj(). For this, we will be having the list collection in C#.

public class objectPool<T> where T : new()
{
// list to hold the objects
private List<T> objectsList = new List<T>();
//counter keeps track of the number of objects in the pool
private int counter = 0;
// max objects allowed in the pool
private int maxObjects = 5;

// returns the number of objects in the pool
public int getCount()
{
return counter ;
}

// method to get the object from the pool
public T getObj()
{
//Declare item
T objectItem;
//Check if the pool has any objects
//If yes, remove the first object and return it
// also, decrease the count
if (counter > 0)
{
objectItem = objectsList[0] ;
objectsList.RemoveAt(0) ;
counter--;
return objectItem;
}
//If the pool has no objects, create a new one and return it
else
{
T obj = new T();
return obj;
}
}

// method to return the object to the pool
// If the counter is less than the max objects allowed, add the object to the pool
//also, increment counter
public void releaseObj(T item)
{
if(counter < maxObjects)
{
objectsList.Add(item);
counter++;
}
}

}

The following is the explanation of the code for a custom object pool class:

  • We declare class variables. objectsList is the pool in which we'll add our object and counter is the number of objects in the pool at a point. maxObjects are the maximum allowable objects in the pool, where the number is up to the programmer.
  • The getObject() method first checks if the pool has any available objects. If it does, it removes the object from the pool and returns it. Otherwise, it creates a new object and returns that.
  • The releaseObj() method checks if the number of objects in the pool is less than the maximum number of objects allowed. If true, it adds the released object to the pool and increments the counter.

Create an Employee class

Let’s create a simple Employee class to see our design in action. This class stores an employee’s first name and ID, with methods to get and set these variables.

class Employee
{
private string firstName;
private int employeeID;

public string getName()
{
return firstName;
}

public void setName(string name)
{
firstName = name;
}

public int getID()
{
return employeeID;
}

public void setID(int ID)
{
employeeID = ID;
}
}

class Program
{
static void Main(string[] args)
{
// declaring an object pool holding class Student objects
objectPool<Employee> objPool = new objectPool<Employee>();

Employee obj = objPool.getObj();

Console.WriteLine("First object assigned");
Employee obj2 = objPool.getObj();
Console.WriteLine("Second object assigned");
int count = objPool.getCount();
Console.WriteLine("Current count of pool: " + count);
objPool.releaseObj(obj);
Console.WriteLine("First object released back into the pool");
count = objPool.getCount();
Console.WriteLine("Current count of pool: " + count);
Employee obj3 = objPool.getObj();
Console.WriteLine("Third object assigned");
count = objPool.getCount();
Console.WriteLine("Current count of pool: " + count);
objPool.releaseObj(obj2);
Console.WriteLine("Second object released back into the pool");
count = objPool.getCount();
Console.WriteLine("Current count of pool: " + count);
objPool.releaseObj(obj3);
Console.WriteLine("Third object released back into the pool");
count = objPool.getCount();
Console.WriteLine("Current count of pool: " + count);
Employee obj4 = objPool.getObj();
Console.WriteLine("Fourth object assigned");
count = objPool.getCount();
Console.WriteLine("Current count of pool: " + count);
}
}

The following is the explanation of the code to demonstrate how object pools work:

  • We declare an object pool named objPool to hold objects from the Employee class.
  • We assign the first and second object as obj and obj2 Since these are the initial instances, the pool is empty; new objects are created and returned.
  • We release the obj back into the pool and check the pool counter. It returns 1, signifying that the obj is successfully added back.
  • We create a third object, obj3, and check the pool counter. As expected, it returns 0 since the new object is taken from the pool instead of creating a new one.
  • Again we release a second object, obj2 , now the pool counter will be 1, and after releasing the third object, obj3 , the pool counter will be incremented to 2.
  • When we create a fourth object, obj4 , the new object is taken from the pool instead of creating a new one and the pool counter will be 1.

Below is the output of the object pool demonstration.

When to use Object Pool Design Pattern

In the following scenarios we can use an object pool design pattern:

  • When there is a necessity to allocate or deallocate many objects.
  • When we know that we have a limited number of objects that will be in memory at the same time.

Below are the pros and cons of the object pool design pattern:

Pros

  • Object pooling helps boost the performance of C# applications by keeping reusable instances of objects in a resource pool. This reduces the overhead of initialization, allocation, and disposal of objects.
  • It manages the connections and provides a way to reuse and share them.
  • Object pool pattern is used when the rate of initializing an instance of the class is high.

Cons

  • The memory size in a heap is fixed for each object. If the objects added to the pool vary in size, there’s memory wastage and the risk of a large object overwriting the next one.
  • As objects are not being cleared by a memory manager (or the garbage collector), the onus falls on the developer to clear the memory. Otherwise, unneeded objects may remain dormant for a long, leading to potential performance overheads.

Conclusion

This article taught us how the Object Pool Design Pattern boosts performance and tracks and recycles expensive objects rather than recreating them whenever the application needs them. It is advantageous to utilize the Object Pool pattern when objects are expensive to create and are needed only for short periods, i.e., if we need a large number of short-lived objects.

--

--