Skip to content

This fix prevents .NET StringBuilder from Large Object Heap allocations, avoids heap fragmentation

Notifications You must be signed in to change notification settings

amikunov/Large-Object-Heap-Fix-For-.NET-String-Builder

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Large Object Heap Fix For .NET StringBuilder

The suggested fix prevents .NET StringBuilder from allocating on the Large Object Heap.

In the current implementation of .NET 4.5 and up each time we call StringBuilder's Append(string value), it internally decides whether to use an existing buffer m_ChunkChars or expand itself by creating another instance of StringBuilder.

If so, AppendHelper method calls ExpandByABlock(int minBlockCharCount) which will obviously allocate on the Large Object Heap if minBlockCharCount is large:

...
m_ChunkChars = new char[newBlockLength];
...

Hence, defeating the purpose of the whole design of StringBuilder as described here:

(https://referencesource.microsoft.com/#mscorlib/system/text/stringbuilder.cs)

// We want to keep chunk arrays out of large object heap (< 85K bytes ~ 40K chars) to be sure.
// Making the maximum chunk size big means less allocation code called, but also more waste
// in unused characters and slower inserts / replaces (since you do need to slide characters over
// within a buffer).  

internal const int MaxChunkSize = 8000;

To fix this issue, we simply call AppendHelper on smaller chunks which will result in allocations on the Small Object Heap ONLY:

private void AppendHelper(string value) {
    unsafe {
        fixed (char* valueChars = value)
        {
            if (value.Length <= MaxChunkSize)
            {
                // regular case
                Append(valueChars, value.Length);
            }
            else
            {
                // possibly large allocation, so do it in smaller chunks
                int numOfChunks = value.Length / MaxChunkSize;
                int remainder = value.Length % MaxChunkSize;
                for (int i = 0; i < numOfChunks; ++i)
                {
                    Append(valueChars + (i * MaxChunkSize), MaxChunkSize);
                }
                if (remainder > 0)
                {
                    Append(valueChars + value.Length - remainder, remainder);
                }
            }
        }
    }
}

Performance

Currently, we check against MaxChunkSize and then call Append(...) at least (value.Length / MaxChunkSize) times. In order to reduce performance impact we can use bigger chunks for allocation, say five times MaxChunkSize.

About

This fix prevents .NET StringBuilder from Large Object Heap allocations, avoids heap fragmentation

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages