Groups | Blog | Home
all groups > dotnet performance > december 2006 >

dotnet performance : C# System.String Memory Usage


mwhalber NO[at]SPAM gmail.com
12/8/2006 4:04:52 AM
Hi,

I have created a service which parses real-time prices from one format
into another. This is all working very quickly and nicely, however, I
have recently found that the memory usage is astronomical after a few
hours. (1.5-2 Gig)

After a good amount of digging I found that the memory usage is is all
in System.String and contained within the pricing object. This object
makes extensive use of a single string object which is passed as an OUT
parameter to a Interop (unmanaged) object.

There are approximately 30 calls to the unmanaged interop object within
the class, and the class is called upwards of 3-4 times per second. I
know that System.Strings are immutable, therefore, I am assuming that
each time this call returns within the class, a new String object is
being created and allocated. The issue is that many of these objects
are being promoted to Gen2 and never being finalized/destroyed by the
GC. The GC apparently assumes that all these string objects are still
alive and will not remove them. I think that this is due to the
frequency at which these objects are being created.

Unfortunately I cannot mitigate the impact using StringBuilder, as the
parameters are OUT string on the interop assembly (this could be
changed by my colleague who wrote it, if neccesary.)

Anyway, does anyone have any idea how to get the memory usage down to a
manageable level?

Kind Regards,
Mike
John Saunders
12/8/2006 11:11:17 AM
[quoted text, click to view]

Are you sure you can't use StringBuilder on an out parameter? I'm pretty
sure I've used it with P/Invoke.

John

miguel
12/11/2006 3:44:12 AM
The unmanaged assembly defines the out parameter as a string when the
interop is created. I could use p/invoke in order to pass a
stringbuilder to the function, however, the assmbly does some work on
an object that is created initially...like so:

class ManagedClass
{

public ManagedClass()
{
UnmanagedClass myUnmanagedClass = new UnmanagedClass();
myUnmanagedClass.DoSomething(); //this manipulates the instance
of the class

string myString = "EMPTY";
myUnmanagedClass.DoSomethingElse( out myString); //this line is
executed about 30 times per instantiation of this class,

//each time creating a new string, as this is an out param and strings
are immutable

}

This is why I don't think I can use p/invoke, as ther is work done on
the instance of the class created from the interop assmbly.

Thanks very much for any help!
Mike




On Dec 8, 4:11 pm, "John Saunders" <john.saunders at trizetto.com>
[quoted text, click to view]
Shawn B.
12/11/2006 9:52:00 AM
have you tried forcing a GC.Collect(2) just to see if it makes a difference?
If so, why not do that on a timer... say, every 15 minutes?


Thanks,
Shawn

Shawn B.
12/18/2006 11:38:41 AM
[quoted text, click to view]

While this might be true (in general) I have a project that actually
improves scalability and performance by calling GC.Collect at the right
place. Without doing so, we can only get about N number of connections
(TCP/IP) on a single server. By doing so, we get about N*5 without a single
noticeable degradation of performance. There's also another place where we
have lots of string handling where placing a GC.Collect in the right place
drastically increases our perceivable performance.

But I agree with you, in general, there probly is a better way. But that
doesn't mean to rule out the possibility. Just because everyone says not
to, doesn't mean you shouldn't see for yourself whether doing so might help
your situation. Of course, this also assumes you understand more about the
GC/JIT/CLR internals than most others do. It also asummes you are a master
of profiling so you can determine whether it helps or not.

YMMV.


Thanks,
Shawn

Henning Krause [MVP - Exchange]
12/18/2006 1:19:15 PM
Hello,

calling GC.Collect is not a good solution. The .NET Framework calls those
methods when necessary, calling it manually will hardly do anything useful
(but some bad things...).

One approach here is to look if the strings are being referenced by another
object. The SciTech memory profiler is a good start here.

Best regards,
Henning Krause



[quoted text, click to view]
Chris Mullins
12/18/2006 4:08:36 PM
Hi Shawn,

If you're getting 5x the TCPIP performance by calling GC.Collect, you're
suffering very badly from heap fragmentation due to all the pinning that's
going on when you send/receive data.

GC.Collect isn't really the right answer there, although I understand how it
could look that way. What you really need is a buffer pool that's all
allocated together, and that you check byte[] buffers into and out of. This
way you don't allocate new byte[] each time you make a socket call - this
causes continual fragmentation, and you'll eventually run out of memory.

The problem is described in detail here:
https://blogs.msdn.com/yunjin/archive/2004/01/27/63642.aspx

A good pair of articles on the subject is here:
http://www.coversant.net/dotnetnuke/Default.aspx?tabid=88&EntryID=9
http://blogs.msdn.com/adarshk/archive/2005/08/20/454022.aspx

.... and the limits of how far you can go is talked about here:
http://www.coversant.net/Default.aspx?tabid=88&EntryID=10

In short (re: your original assertion) the only time you really want to call
"GC.Collect" is just before you're forced to grow your buffer pool. When you
do grow, you want to grow by a large amount (1k, 5k, 10k instances of
whatever your buffer size is) so that any holes in your heap are all close
together. This leverages the GC.Collect mostly to compact the heap, so that
your buffers are allocated as low as possible.

--
Chris Mullins, MCSD.NET, MCPD:Enterprise
http://www.coversant.net/blogs/cmullins


[quoted text, click to view]

Shawn B.
12/18/2006 4:13:04 PM
Thanks Chris.





Thanks,
Shawn





[quoted text, click to view]

Phil Wilson
1/9/2007 12:23:16 PM
You're right - StringBuilder is the way to get [out] string values. I think
it's fair to say that every [out] string item on pinvoke.net uses
StringBuilder for the returned string.
--
Phil Wilson
[Microsoft MVP Windows Installer]

[quoted text, click to view]

AddThis Social Bookmark Button