开发者

StringBuilder exception on changing Capacity!

开发者 https://www.devze.com 2023-03-18 23:15 出处:网络
here is my code line: StringBuilder myCompleteMessage = new StringBuilder(); myCompleteMessage.Capacity = Int32.MaxValue-1;

here is my code line:

StringBuilder myCompleteMessage = new StringBuilder();
myCompleteMessage.Capacity = Int32.MaxValue-1;

tryed that also:

myCompleteMessage.Capacity = myCompleteMessage.MaxCapacity-1;

I get exception on line 2.

Exception of type 'System.OutOfMemoryException' was thrown.

Stack Trace:

at System.String.GetStringForStringBuilder(String value, Int32 startIndex, Int32 length, Int32 capacity)
at System.Text.StringBuilder.set_Capacity(Int32 value)
at Orca.Server.HandleClientComm(Object newClient) in C:\Users\Dan\Documents\Visual Studio 2010\Projects\msig\Orca\Server.cs:line 100
at System.Threading.ExecutionContext.Run(Exe开发者_如何学运维cutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart(Object obj)


Assuming you are on a 32bit system, that second line will always fail. You're asking .NET to allocate 4 GB of space to your StringBuilder, which is more memory than the process has to work with (thanks to Joel for pointing out char takes up 2 bytes, not 1).

EDIT
If you take a look at StringBuilder with ILSpy, you see this bit of code in the set for Capacity:

if (this.Capacity != value)
{
    int num = value - this.m_ChunkOffset;
    char[] array = new char[num];
    Array.Copy(this.m_ChunkChars, array, this.m_ChunkLength);
    this.m_ChunkChars = array;
}

By setting Capacity to int.MaxValue - 1, you're telling .NET to try and allocate a 4 GB char array, which is why the code is failing.


CLR heap is limited to 2GB objects ( http://blogs.msdn.com/b/joshwil/archive/2005/08/10/450202.aspx - for 2.0, I believe it the same for 4.0) so there is nothing that can allocate one continious block of memory above that size. For characters it gives you about Int.MaxValue/2 entries.

If you really need to manage such amount of text check out alternative implementations of MemoryStream that allow chunking or arrays that allow chuncking. If you want to stay within default classes - consider writing data to temporary file (temporary file created with DeleteOnClose may not be even commited to disk - so you get better performance compared to StringBuilder or MemoryStream that have to copy data on each increase in capacity - http://msdn.microsoft.com/en-us/library/system.io.fileoptions.aspx).


StringBuilder's Capacity property is the number of characters to set aside as the string buffer. With UTF-8, a character can be up to 4 bytes. Even at 2 bytes, a capacity of Int32.MaxValue - 1 is well over capacity for a 32-bit system, weighing in at 4 GB for just 2-byte characters (UTF-8/ASCII). Also, StringBuilder's default Capacity is already set to Int32.MaxValue if you look at the documentation.

If you are looping to fill the StringBuilder, you may be filling it faster than .NET can clean up allocated memory through garbage collection, which is why it may be stopping at the 2048 character length. Also, internally it needs a contiguous block of memory, which it also may be running into an issue with.

Your question, though, is about the Capacity property, and you'll never be able to allocate that much capacity on a 32-bit system. I found an interesting discussion on MSDN dealing with this specific problem and through testing it was found that actual capacity is a great deal lower since Int32.MaxValue capacity translates to 4 GB of storage necessary when all characters are 2-bytes.

Can you use a Memory-Mapped file instead? StringBuilder's were not meant for this and you need to reconsider your design.

0

精彩评论

暂无评论...
验证码 换一张
取 消