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 onObjectPool
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 theObjectPool
, 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.