Table of Contents

StringBuilderCache

Since strings in .NET are immutable, building out strings from multiple parts can be memory expensive. Strings are one of the most memory-costly types in the entire system because they hold many characters, so building strings with many intermediate steps can be very expensive because an entirely new string instance is allocated for each step. The Concat method on the String type is helpful in cases where a small number of string segments that are immediately available are being concatenated. Still, it is inefficient to build strings from many parts with logical processing between each step.

The StringBuilder class is a mutable string class that can be used to build strings more memory efficiently. However, the StringBuilder class has its own memory issues, as it can still allocate a lot of memory for its internal buffer and for the StringBuilder instances themselves.

The .NET core library has an internal helper class called StringBuilderCache that relieves some of this memory pressure. Still, there are many use cases in every line of business application where caching StringBuilder instances would help application performance, and that internal caching class is not available publicly.

The StringBuilderCache class in this library is a similar static class that provides a pool of StringBuilder instances that can be reused. This can be very helpful in scenarios where many StringBuilder instances are created and disposed of frequently, such as in a loop or an often called method. This caching can have a significant positive impact on applications for overall performance as well as less memory pressure.

Usage

Using StringBuilderCache is very simple. The class provides a static method, Acquire, that returns a StringBuilder instance from the pool. When you are done with the StringBuilder instance, you return the instance to the pool either directly with a call to the Release method on the StringBuilderCache class, or you can get the built string and return the StringBuilder instance to the pool in one step with the GetStringAndRelease method.

using KZDev.PerfUtils;

class Program
{
	static void Main()
	{
		StringBuilder stringBuilder = StringBuilderCache.Acquire();
		stringBuilder.Append("Hello, ");
		stringBuilder.Append("World!");
		Console.WriteLine(stringBuilder.ToString());
		StringBuilderCache.Release(stringBuilder);
	}
}

or

using KZDev.PerfUtils;

class Program
{
	static void Main()
	{
		StringBuilder stringBuilder = StringBuilderCache.Acquire();
		stringBuilder.Append("Hello, ");
		stringBuilder.Append("World!");
		Console.WriteLine(StringBuilderCache.GetStringAndRelease(stringBuilder));
	}
}

To ensure that the StringBuilder instance is returned to the pool, you must call the Release method on the StringBuilderCache class or use the GetStringAndRelease method. If the StringBuilder is not returned to the cache, it will simply get garbage collected like any other object, but you will not get the full performance benefit of having the cache in those cases.

For cases where an exception may be thrown and to be sure the StringBuilder is still returned to the cache, you should use a try-finally block to release the instance.

using KZDev.PerfUtils;

class Program
{
	static void Main()
	{
		StringBuilder stringBuilder = StringBuilderCache.Acquire();
		try
		{
			stringBuilder.Append("Hello, ");
			stringBuilder.Append("World!");
			Console.WriteLine(stringBuilder.ToString());
		}
		finally
		{
			StringBuilderCache.Release(stringBuilder);
		}
	}
}

Alternatively, you can use the StringBuilderCache class with the using statement to get an instance of StringBuilderScope to ensure that the StringBuilder instance is returned to the pool when you are done with it.

using KZDev.PerfUtils;

class Program
{
	static void Main()
	{
		using StringBuilderScope builderScope = StringBuilderCache.GetScope();
		StringBuilder builder = builderScope.Builder;
		builder.Append("Hello, ");
		builder.Append("World!");
		Console.WriteLine(builderScope.ToString());
	}
}

Note: The StringBuilderCache class is thread-safe so that you can use it in multi-threaded scenarios without issue.

Monitoring

StringBuilderCache provides the ability to monitor the cache usage using the Events feature of the .NET runtime. Read More