Object pooling is a software creational design pattern and a container of objects that holds a list of other objects—those are ready to be used. Once an object is taken from the pool, it is not available in the pool until it is put back. Object pooling keeps track of Objects—those are currently in use, the number of objects the pool holds, and whether this number should be increased. Objects in the pool have a lifecycle of creation, validation, and destroying.
.NET Object Pooling
When building a .NET application, most often developers come across objects that are quite expensive to create. In most scenarios, the cost of creating new objects is high enough to impact application performance. The object pool design pattern is applicable in that scenario. Keeping reusable instances of objects in a resource pool and using them out as needed boosts the performance of a .NET application. During the request of an object to use from the application, if the object is available from the pool, it is returned from the pool. In case the object requested by the application is not available from the pool, a new instance of the object is created and returned to the source program.
Creating an Object Pool in C#
To demonstrate Object pooling, I will be using a factory pattern. I will create a factory method, which will take care of creating the objects. During a request for a new object, the factory method will look into the object pool, which is a queue of objects. If there is any object available within the allowed limit, it will return the object; otherwise, a new object will be created and return back.
The following .NET console application code snippet example will explain object creation, adding it into the queue, and returning it from the queue.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
namespace PrjObjectPooling
{
class Program
{
static void Main(string[] args)
{
Factory fa = new Factory();
Student myStu = fa.GetStudent();
Console.WriteLine("First object");
Student myStu1 = fa.GetStudent();
Console.WriteLine("Second object");
Student myStu2 = fa.GetStudent();
Console.WriteLine("Third object");
Console.Read();
}
}
class Factory
{
// Maximum objects allowed!
private static int _PoolMaxSize = 3;
// My Collection Pool
private static readonly Queue objPool = new
Queue(_PoolMaxSize);
public Student GetStudent()
{
Student oStudent;
// Check from the collection pool. If exists, return
// object; else, create new
if (Student.ObjectCounter >= _PoolMaxSize &&
objPool.Count > 0)
{
// Retrieve from pool
oStudent = RetrieveFromPool();
}
else
{
oStudent = GetNewStudent();
}
return oStudent;
}
private Student GetNewStudent()
{
// Creates a new Student
Student oStu = new Student();
objPool.Enqueue(oStu);
return oStu;
}
protected Student RetrieveFromPool()
{
Student oStu;
// Check if there are any objects in my collection
if (objPool.Count > 0)
{
oStu = (Student)objPool.Dequeue();
Student.ObjectCounter--;
}
else
{
// Return a new object
oStu = new Student();
}
return oStu;
}
}
class Student
{
public static int ObjectCounter = 0;
public Student()
{
++ObjectCounter;
}
private string _Firstname;
private string _Lastname;
private int _RollNumber;
private string _Class;
public string Firstname
{
get
{
return _Firstname;
}
set
{
_Firstname = value;
}
}
public string Lastname
{
get
{
return _Lastname;
}
set
{
_Lastname = value;
}
}
public string Class
{
get
{
return _Class;
}
set
{
_Class = value;
}
}
public int RollNumber
{
get
{
return _RollNumber;
}
set
{
_RollNumber = value;
}
}
}
}
Object pooling is very similar to database connection pooling. Remember, the minimum and maximum number of objects that could be added in an object pool is configurable. During design, your pool following strategies could be implemented:
Return null or throw an exception if the application needs an object from the pool but the maximum number of objects has been allocated.
Block the call until an object is available.
Increase the pool size to accommodate more objects.
A developer can modify this custom object pool implementation to allow the minimum and maximum sizes of the object pool to be read from a configuration file. As part of initialization of the object pool, we also can ensure that the pool contains the minimum number of objects in it.
Object pools help to reduce resource overhead when you need multiple instances of a class that are expensive to create or manage. If your application involves instantiating the same classes over and over again, use this design pattern to ensure optimal performance.