views:

117

answers:

7

Hi All,

I have my website written using ASP.NET. If I let it, the website will hit about 2gb physical memory used within about 3-4 days which to me sounds really bad. At the moment, I have told IIS to restart the process when it hits 500mb just to keep it under wraps but I would like to try and track down the problem.

When creating a new instance of an object in .NET, I was under the impression that it doesn't need to be free'd as the .NET garbage collector will do this for me, is that the case or could this be one of the reasons I am having issues?

Thanks.

+4  A: 

There could be lots of reasons for your high memory usage, but Garbage Collection in .NET is a very precise thing. That is, it does a lot for you, but sometimes not as much as you'd expect.

Specifically, it can only clean up objects to which there are no active references, so if you're done with a class, but something still has a reference to it, you'll want to remove that reference so the GC can recover that memory for you. Additionally, if you have any inactive open connections (say, to a DB or something) don't forget to close and dispose them. Typically, we wrap such objects in using blocks like this:

using(SqlConnection conn = new SqlConnection(myConnString))
{ ...rest of code here }

This will automatically close and dispose of the connection; I'm pretty sure it also gets you Try/Finally for free (somebody double-check me on that, please).

Other than that, the answer is "profile, profile, profile" until you find your leak/bottleneck/whatever.

AllenG
+1 for profile profile profile
Chris Marisic
Yep, using the `using` statement is equivalent to using a `try`-`finally` block with a call to `Dispose` in the `finally` block.
Steven
@Steven: Thanks for that.
AllenG
A: 

Whilst it is true that you don't have to explicitly free memory and the garbage collector will do this for you it is possible for code in inadvertently hold a reference to an object to prevent it being collected - this is essentially a memory leak.

In ASP.NET objects remain "alive" whilst they are referenced by the Application and Session cache. Without knowing anything about your application I would suggest you get hold of a memory profiler and look more closely at exactly what is in memory and whether the Application or Session cache is holding onto something it shouldn't.

TortoiseTNT
+1  A: 

Memory leaks in .NET are still possible. It is true that for the most part you don't need to free objects (there are a few exceptions such as the Graphics object) but if you keep references to them, they will not get garbage collected, because they are still referenced.

If the GC sees that an object is being referenced somewhere, anywhere in your app, it will not trash it.

Check out this Code Project article on .NET memory leaks and how to go about locating and fixing them.

Paul Sasik
+1  A: 

.NET will manage garbage collection for you very efficiently. While on types that implement IDisposable it is whise to call the Dispose method, this propably isn't your problem. Memory leaks can happen in .NET for a lot of reasons. Here are a few:

  • You cache too much data per user in the Session.
  • You cache too much data on an application level in the application cache or in a static variable such as a dictionary.
  • You store web controls (or user controls) in the session or application level.
  • You hook instances to events on static types or to types that keep being referenced (because they are stored in a cache).

I hope this gives you some ideas about where to look.

UPDATE: You should watch this video about ASP.NET debugging.

UPDATE 2: About your comment on my answer the following. The CLR will collect all managed memory, therefore all object you create using new will get collected. In this sense, it doesn't matter whether an object implements IDisposable or not. There are however many times that you need to use native resources (such as file handles, graphic handles, database connections, use of native -thus unmanaged- memory) directly or indirectly. The CLR does not know how to release these resources. For this .NET has the notion of finalizers. A finalizer is a virtual method that a the developer of a class can implement. When you do this, the CLR will call this method after an instance of that type gets unreferenced and before it gets collected. Finalizers typically contain logic that release these resources. In other words, when a type needs native resources, it will usually have a finalizer method that allow the type to release those resources.

What about the CLR is concerned, the story ends here. The CLR has no specific handling of objects that implement the IDisposable interface. The .NET garbage collector however, is undeterministic in nature. This means that you don't know when it runs and if it runs. This means that it can take a very long time before your native resources get cleaned up (because a finalizer will only get called after a collect). For many resources however, it is necessary to release them as soon as your done with them. For instance, you tend to run out of database connections quickly when you don't close them or when you’re working with GDI+ in .NET through the System.Drawing namespace).

For this reason the IDisposable interface was introduced. Again, the CLR and the garbage collector don't look at this interface. It is a contract between the type and its users, allowing its users to directly release the underlying resources of an object. In a normal design both the object's finalizer and the object's Dispose method will call the same private or protected method that will release those resources. When a type implements IDisposable it is wise to call it's Dispose method when you’re done with it or wrap the object in a using statement to allow the release of native resources to be deterministic.

So to come back to your question. All managed objects will be collected by the GC, but native resources won't. Therefore types might implement a finalizer method and those objects will also typically implement the IDisposable interface. Calling Dispose on them will explicitly and directly release those native resources.

I hope this makes sense.

Steven
Does this mean that only classes that inherit from IDisposable will be GC'd?
webnoob
@Webnoob: see my update.
Steven
I think you may have just found the cause of my problem :) All images on my website are run through a custom handler I have written that creates a thumbnail of that image. From what I can remember, I am not doing anything clean up on that code and it definitely uses the system.drawing methods. Great place to start for me. Thanks for the post. Have marked this one as an answer as it gives me the most information I need at the moment.
webnoob
If you are in doubt, you can always post a new question here at SO and show the code in question. Good luck.
Steven
Thanks Steven, I have posted another question: http://stackoverflow.com/questions/3415693/system-drawing-and-garbage-collection just to clear something up with regards to what to dispose of.
webnoob
+1  A: 

There are a few things you should look at :

First of all, are you using sessions? Are they in proc or SQL sessions? If they are in process, what's the timeout set at? If you have a really really long timeout, this could explain why you are using so much memory (the user sessions would be stored for a long time).

Second, disposing of objects. The .NET garbage collector will get rid of references for you, but when you are creating objects that implement the IDisposable interface, you should always use the using keyword.

using(Foo foo = new Foo())
{
    ...
}

is the equivalent of doing :

try
{
    Foo foo = new Foo();
    ...
}
finally
{
    foo.Dispose();
}

And it will ensure that you dispose of your objects efficiently.

If you still can't find anything obvious in your code, you can profile it, starting with the methods that are called the most. You can find information about good profilers here. That will definitely lead you to the source of your problem.

Hugo Migneron
A: 

If your ASP.NET application uses events (and which one doesn't), then you have to make sure you unsubscribe from the event.

Rule of thumb:

If you use += to subscribe to an event, then you need to use -= in the Dispose() method to unsubscribe.

There are lots of resources on this topic, and I've had a bit of pain in applications I've worked in because your run-of-the-mill programmer doesn't realize that events can cause memory leaks.

George Stocker
+1  A: 

Use a memory profiler (either on a memory dump, or on your live environment) when your memory exceeds that 500 MB to get a clue which objects take up all that space.

When profiling on your development environment you might see one particular objecttype only grow when surfing around on your site whilst others remain globally the same.

Jan Jongboom