ObjectPool for resource management

Using the ObjectPool class to keep objects in memory for reuse

Home DailyDrop

Daily Knowledge Drop

Th ObjectPool class can be leveraged to keep a group of objects in memory for reuse rather than allowing the objects to be garbage collected and having to be reinitialized every time required.

This is especially useful if the object:

  • is expensive to allocate/initialize.
  • represent some limited resource.
  • is used predictably and frequently.

Usage

Usage of the ObjectPool consists of a few steps:

ObjectPoolProvider

The ObjectPoolProvider is used to ultimately create the ObjectPool based on a policy. The below code snippet uses the DefaultObjectPoolProvider.

The dependency injection container is configured as follows:

builder.Services.TryAddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();

When the DefaultObjectPoolProvider is used and the item stored in the pool (StringBuilder in the following examples) implements IDisposable, Then:

  • items that are not returned to the pool will be disposed
  • when the pool gets disposed by the dependency injection container, all items in the pool are disposed

ObjectPool

The next step is to register the ObjectPool itself with the dependency injection container, in this case using an implementation factory:

builder.Services.TryAddSingleton<ObjectPool<StringBuilder>>(serviceProvider =>
{
    // get the provider registered in the previous step
    var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
    // create a policy, using the built in string builder policy
    var policy = new StringBuilderPooledObjectPolicy();
    // return an ObjectPool<StringBuilder> instance created using the policy
    return provider.Create(policy);
});

Injection

ObjectPool<StringBuilder> can now be injected into the relevent method/delegate:

app.MapGet("/buildstring", (ObjectPool<StringBuilder> pool) =>
{
    // get a StringBuilder instance from the pool
    var sBuilder = pool.Get();

    try
    {
        // use the string builder
        sBuilder.Append("This string has been built up using ");
        sBuilder.Append("a StringBuilder instance from an ");
        sBuilder.Append("ObjectPool<StringBuilder> instance");

        return sBuilder.ToString();
    }
    finally
    {
        // return it to the pool
        pool.Return(sBuilder);
    }
});
  • The Get method on ObjectPool is used to get an instance of the class from the pool (StringBuilder in this example)
  • The Return method is used to return the instance back to the ObjectPool, making it available for reused in the future

Performance

While using the ObjectPool can definitely increase performance, it does not always guarantee it. Object pooling usually won't improve performance:

  • unless the initialization cost of an object is high, it's usually slower to get the object from the pool.
  • objects managed by the pool aren't de-allocated until the pool is de-allocated.

Notes

Under the right use-case object pool can definitely make a difference to the performance of an application. However, if used incorrect it can be detrimental - so if performance is a concern, benchmark and make an informed decision for the specific use case.


References

Object reuse with ObjectPool in ASP.NET Core

Daily Drop 181: 13-10-2022

At the start of 2022 I set myself the goal of learning one new coding related piece of knowledge a day.
It could be anything - some.NET / C# functionality I wasn't aware of, a design practice, a cool new coding technique, or just something I find interesting. It could be something I knew at one point but had forgotten, or something completely new, which I may or may never actually use.

The Daily Drop is a record of these pieces of knowledge - writing about and summarizing them helps re-enforce the information for myself, as well as potentially helps others learn something new as well.
c# .net object pooling objectpool