this is my first post here. I'm making a game in Visual Studio 2010 using XNA, and i've hit a giant memory leak. My game starts out using 17k ram and then after ten minutes it's upto 65k. I ran some memory profilers, and they all say that new instances of the String object are being created, but they aren't live. The amount of live instances of String hasn't changed at all. It's also creating instances of Char[] (which i'd expect from it), Object[], and StringBuilder. My game is pretty new but there's too much code to post here. I have no idea how to get rid of instances that aren't live, please help!
-
5I'm not sure I would consider 65k a "giant memory leak". You could almost fit that on a personal computer from 30 years ago. If it was blowing up to 650MB I might be a little worried. – Andy West Jun 06 '12 at 22:39
-
I'm guessing you must mean up to 65,000K, as I don't think you can even get down to a 17K hosted process size anymore. Even then, 65M is not all that big by modern standards. – Dan Bryant Jun 06 '12 at 23:16
-
Okay, we'll if that's okay then illjust let it slide. It does reset occasionally. Thanks so much! And yes I meant 65,000k lol – Kirbyfanner Jun 07 '12 at 00:39
2 Answers
You haven't posted enough information to provide you with more than an educated guess. Here is my educated guess:
If you are doing things like this in your Draw method:
spriteBatch.DrawString(font, "Score: " + score, location, Color.Black);
spriteBatch.DrawString(font, "Something else: " + foo, overHere, Color.Black);
spriteBatch.DrawString(font, "And also: " + bar, overThere, Color.Black);
Then each of those calls will be creating new string
and StringBuilder
objects behind your back each time they run. Because they are in your Draw
method, each is probably running 60 times per second. That is a lot of temporary objects being allocated!
To verify that this is the case - use the CLR Profiler. It sounds like you have already done this.
While this isn't really a "leak" - the Garbage Collector will eventually clean them up - this allocation pattern is undesirable in a game. See this blog post about two methods for dealing with garbage collection in a game. Method 1 is usually easier and provides better results - so I am discussing it here.
It is worth mentioning at this point that the GC on the PC is fast enough that allocations like this don't really matter. The GC will clean up tiny objects (like your temporary strings) with very little overhead.
On the Xbox 360, on the other hand, even producing tiny amounts of garbage like this regularly can cause some serious performance issues. (I'm not sure about WP7, but I would personally treat it like the Xbox -- with care!)
How do we fix this?
The answer is simple: DrawString
will accept an instance of StringBuilder
in place of string
. Create one instance of StringBuilder
and then reuse it each time you need to put together a custom string.
Note that converting a number or other object to a string, implicitly or by its ToString()
method will also cause allocations. So you might have to write your own custom code to append to StringBuilder
without causing allocations.
Here is one I use, in the form of an extension method, for appending integers to a string without allocating:
public static class StringBuilderExtensions
{
// 11 characters will fit -4294967296
static char[] numberBuffer = new char[11];
/// <summary>Append an integer without generating any garbage.</summary>
public static StringBuilder AppendNumber(this StringBuilder sb, Int32 number)
{
bool negative = (number < 0);
if(negative)
number = -number;
int i = numberBuffer.Length;
do
{
numberBuffer[--i] = (char)('0' + (number % 10));
number /= 10;
}
while(number > 0);
if(negative)
numberBuffer[--i] = '-';
sb.Append(numberBuffer, i, numberBuffer.Length - i);
return sb;
}
}

- 26,924
- 7
- 58
- 104
-
You technically don't need numberBuffer; just append 1 char at a time (faster due to reduce cache hits). If you are concerned about resizing, use sb.EnsureCapacity(sb.Length + 16) to prevent it (16 to retain cache line alignment). – GGulati Jun 07 '12 at 02:54
-
Oh wow, this makes so much sense. I'll try this as soon as I can, thanks so much! – Kirbyfanner Jun 07 '12 at 14:47
-
@GGulati While you are right that calling `Append` on characters performs slightly better than building the string in a buffer and dropping it into place, **note that the algorithm is building the string right-to-left**. I'll leave figuring out a left-to-right algorithm as an exercise ;) Furthermore: no point worrying about `EnsureCapacity`. The whole point here is to reuse a buffer. So getting the capacity up will happen once. And the CPU time is such a tiny difference. This is a specific GC optimisation. Bad form to layer unnecessary extra optimisations on top. – Andrew Russell Jun 08 '12 at 12:37
There are no memory leaks in C# (or well, they're very difficult to get). What you are experiencing is normal. The garbage collector doesn't "feel" like it needs to collect memory, so it does not. Whenever memory runs short, garbage collection will occur. If you're absolutely certain that you aren't keeping unneeded references to your string
s, then everything's fine.
If you want to force a GC cycle use GC.Collect()
.

- 37,307
- 8
- 87
- 112
-
6Using `GC.Collect` in a game is not a good idea, it's better to use object pooling inside the game loop and just avoid dynamic allocations. – Daniel Little Jun 06 '12 at 23:32
-
Some part of XNA is not managed code, so the GC will not work. like this : http://stackoverflow.com/questions/3364481/outofmemory-exception-when-drawing-cube – Steven Du Jan 06 '13 at 16:02