Sign in to follow this  

[.net] How do I improve memory usage in C# ?

This topic is 4534 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, After my first game in C#, I've notice the memory problem. Anytime I click on the New game button, the memory using increased (I use Task manager to track) and the Mem usage column always show the same value as in Peak Mem Usage. That's not normal, isn't it? Well, after i implement IDisposable for every game object, the amount of memory increased is not as much as before, but the Mem Usage keeps topping at the Peak Mem Usage, I don't know why. I can keep pushing New Game and see Mem Peak and Mem Usage both goes up to 100MB or more, but they don't fall down. How do I improve that?

Share this post


Link to post
Share on other sites
Check out the Clr Profiler. It will let you track where your memory allocations are coming from.

Implementing IDisposable won't actually gain you anything. And if you implement a finalizer, it will actually delay the garbage collection of your objects, since they will have to spend one collection in the finalizer queue.

Share this post


Link to post
Share on other sites
IDisposable allow you to control more easily the memory usage. But if you implement it, you must call Dispose() on every object that implments it. Otherwise you wont gain anything as sjelkjd said, objects that have a finalizer that is not called by code will survive at least 2 pass of GC, which will lead to more object being promoted to generation 2 and make memory usage even worse. But as long as you call Dispose() on every object you should be fine.

Share this post


Link to post
Share on other sites
You should NOT be using IDisposable to free up memory - it is meant to be used with unmanaged resources such as window handles, file streams, etc. Think about it - pretty much the only useful thing you could do is set all references in the class to null, so they'll be collected during the next pass. However, if you're disposing your object, it you shouldn't be holding on to a reference to it, since the object will be invalid. And if there is no reference to it, then the GC will collect it during the next pass - and it will also collect any objects in the class. So implementing IDisposable to try to handle managed memory is a waste of time.

Also, you should try to find out the frequency of garbage collections. You can check this with the performance monitors (control panel - admin tools - performance). You can add a counter for garbage collections(in the .net clr memory category), and look at % time in GC. This should ideally be no greater than about 15% for your application. If it is much higher, then something is wrong - you're thrashing the memory manager. Rico Mariani(CLR perf architect) calls this Mid life crisis.

Share this post


Link to post
Share on other sites
Thanks for your supports.

I understood the use of Dispose() and Finalize(). What I did in my game was implement IDisposable for every object that has a mesh, and call its Dispose() in the "game object container". I thought Mesh, Device, AudioPlayer etc... all have unmanaged resources bundled with them, so...


Maybe I'll check over all the source code again.

Share this post


Link to post
Share on other sites
Quote:
Original post by vincent_valentine
Thanks for your supports.

I understood the use of Dispose() and Finalize(). What I did in my game was implement IDisposable for every object that has a mesh, and call its Dispose() in the "game object container". I thought Mesh, Device, AudioPlayer etc... all have unmanaged resources bundled with them, so...


Maybe I'll check over all the source code again.


Well, it sounds like that part is working as it should. Have you used CLRProfiler? It gives you a ton of very useful information for tracking down memory usage.

Share this post


Link to post
Share on other sites
Yes they all have unmanaged resources associated with them. But their Dispose() methods will all be called during finalization, so if you don't care about when they are disposed, you don't have to worry about it.

That being said, watch out for when DirectX references something even though you don't. For example, the Direct3D device references all resources (or maybe only managed resources) so that it can maintain them internally. What this means is that even if YOU don't reference them, D3D still does, and they won't be disposed until the D3D device is disposed. So yes, if you want to destroy these sooner, you'll have to call dispose yourself on them. This is a problem that acounted for a huge memory leak in my application that took me forevery to fix, so watch out.

Also note that the CLR profiler only shows you managed memory usage, not unmanaged. And while the D3D resources are probably contained within managed wrappers, the amount of memory actually being used by one of those wrappers is going to be much higher than what the profiler claims.

Share this post


Link to post
Share on other sites
Quote:
Original post by Holy Fuzz
What this means is that even if YOU don't reference them, D3D still does, and they won't be disposed until the D3D device is disposed. So yes, if you want to destroy these sooner, you'll have to call dispose yourself on them. This is a problem that acounted for a huge memory leak in my application that took me forevery to fix, so watch out.


Holy Fuzz, can you explain it more clearly? This seems like an interesting experience...


In my game app, I've tried not to call Dispose() manually, and the memory effect remains the same. So they're called automatically anyway, true.

Anyway, here's the link to the post to download my game, it's called Tetris#
Link to my game

[Edited by - vincent_valentine on July 6, 2005 5:58:24 AM]

Share this post


Link to post
Share on other sites
Okay, so here's my best guess as to what's happening (if anyone has any other ideas or knows for sure, then please say so)...

The memory for a .Net application is divided into two sections: one for managed resources (your regular old objects) and one for unmanaged resources.

Now, DirectX is an *unmanaged* API, and hence all of the resources it creates are allocated into the section for unmanaged resources. This includes things like DX-managed (not to be confused with .Net-managed) textures and vertex buffers, which are stored in system memory alongside video memory so that when the device is lost DX can automatically restore them without any hastle.

"Managed DirectX" is merely a .Net wrapper around the normal, unmanaged DX. What this means is that when you create a texture in managed DX, most of the memory allocated for it is actually unmanaged, and only a small fraction (the wrapper itself) is managed.

Now, as you already know, when a wrapper to a DX resource such as a texture is garbage collected (or we call Dispose), the wrapper also releases the unmanaged resources associated with it.

But this doesn't tell us why your app is using so much memory...

One possibility (I am not sure about this, so maybe someone could confirm?) is that the managed-DX wrapper itself contains references to the managed wrappers around the various unmanaged resources (textures, vertex buffers, etc...), which means that NONE of the resources will be garbage collected (and disposed) until the managed-DX wrapper itself is disposed. Hence, if this is the case, none of your resources will be released until either you manually dispose them or the DX device itself is disposed.

The other possibility (and I'm pretty certain that this is happening) has to do with how the garbage collector decides to collect unreferenced objects. If you don't already know, all of your managed objects are divided into 3 generations. All newly created objects are created in gen 0. When your app runs out of memory, it collects all gen 0 objects that are unreferenced, and if afterwards it has enough available memory to continue without further garbage collections of any higher generations, it simply stops. Any object which survives a gen 0 collection is moved to gen 1, and so on...

Now, note that the garbage collector only keeps track of *managed* memory usage, and not unmanaged memory usage. This means that large allocations of unmanaged memory will NOT trigger garbage collections, and the memory allocated by the managed wrappers around those resources is so small that they too very infrequently trigger garbage collections. Also, these resources are very likely to be promoted to gen 1 or even gen 2, meaning that gen 0 collections won't reach them, and thus that they will not be disposed and WILL NOT RELEASE THEIR UNMANAGED RESOURCES. Since the garbage collector does not keep track of unmanaged resources, allocation of texture and vertex buffers appear to the GC to have a much smaller memory footprint than they actually do.

Please keep in mind that both of the possibilities I arrived at via my own experience and relatively limited knowledge about the CLR. If someone knows better than I, please speak up.

- Fuzz

Share this post


Link to post
Share on other sites
You know, without measuring, guessing is not really going to take you very far. You should measure your memory allocations, and see where it's coming from. If it's not showing up as managed allocations, then you should look at unmanaged resources. But just guessing without data is a waste of time.

Share this post


Link to post
Share on other sites
Thanks for the guess and the long post, Fuzz.
In the mean time, please try my game, because I have another problem here:

- Running the executable file on a diffirent machine than mine pops out an error message:
Application has generated an exception that could not be handled

This is not the topic, but please have a look as well. I think the problem is i'm using VS.NET 2002 to build.

Share this post


Link to post
Share on other sites
Hey I've fixed it.

It's a problem with VS.NET 2002. Now everything's running smooth.

I've taken a look over to the memory issue, but seems like nothing's wrong. If someone could test my game again to see if there are any trouble, i'd be most appreciate.

Share this post


Link to post
Share on other sites
!@#$, it hasn't been fixed yet. I've try it on several PC, but only a few works. The error messages are different on different machines, and don't know how to get it all together.

So anyone out there, please try to download my game and run it. I want to make sure that my game can run smoothly on EVERY PC, so that everyone can play.

Change the file name to .zip after you download

Share this post


Link to post
Share on other sites
Thank you, joanusdmentia, it really boost my spirit. You're the first "stranger" to play my game. Well now we're not strangers anymore ;)

You have the exact same runtimes as I do. Maybe that's the key. Anyone else want to try out?

Share this post


Link to post
Share on other sites
Quote:
Original post by vincent_valentine
Quote:
Original post by Holy Fuzz
What this means is that even if YOU don't reference them, D3D still does, and they won't be disposed until the D3D device is disposed. So yes, if you want to destroy these sooner, you'll have to call dispose yourself on them. This is a problem that acounted for a huge memory leak in my application that took me forevery to fix, so watch out.


Holy Fuzz, can you explain it more clearly? This seems like an interesting experience...


In my game app, I've tried not to call Dispose() manually, and the memory effect remains the same. So they're called automatically anyway, true.

Anyway, here's the link to the post to download my game, it's called Tetris#
Link to my game


C# has a nice keyword for automatically disposing of IDisposable objects 'using'.
example:

using (MyDisposableObj tempObj = new MyDisposableObj ())
{
tempObj.Foo();
}

When using goes out of scope, Dispose() is automatically called on tempObj.

Share this post


Link to post
Share on other sites
Great. Holy Fuzz was able to play, so I think the problem has been fixed.

Actually, yesterday I was looking around, typing the exact error message in Google. I found a site which said: The Managed DirectX sometimes failed to install, causing File or assembly not found error. This problem has been fixed since the June update. I only have the Feb update, so I think that I must get a newer SDK of DirectX.

@Madmunki: I knew of the pattern you shown. It's just hard to fit in some situation. But it's good when you knew exactly how the object will be release.

To sum it all up I think the solution is to use the "new" keyword with caution.

I found the Dispose() implementation sample in the MSDN. I'll post it here:


/// <summary>
/// Dispose the object
/// </summary>
public void Dispose()
{
Dispose(true);
// Take yourself off of the Finalization queue
// to prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}

// Dispose(bool disposing) executes in two distinct scenarios.
// If disposing equals true, the method has been called directly
// or indirectly by a user's code. Managed and unmanaged resources
// can be disposed.
// If disposing equals false, the method has been called by the
// runtime from inside the finalizer and you should not reference
// other objects. Only unmanaged resources can be disposed.
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// Dispose unmanaged resources here
}
this.managedResource.Dispose();
}


Share this post


Link to post
Share on other sites
Quote:
The Managed DirectX sometimes failed to install, causing File or assembly not found error. This problem has been fixed since the June update.


My understanding is that DX redistributables prior to the June2005 release did not install managed DX by default (you had to specify a special command-line argument or run the .msi seperately). The June redist does install it by default, as long as the .Net framework is also present.

Share this post


Link to post
Share on other sites

This topic is 4534 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this