views:

503

answers:

7

I guess this is very basic but since I'm learning .NET by myself, I have to ask this question.

I'm used to coding in C, where you have to free() everything. In C++/.NET, I have read about the garbage collector. From what I understand, when an instance is no longer used (in the scope of the object), it is freed by the garbage collector.

So, having that in mind, I built a little testing application. But, it seems I didn't get something because when doing the same things a few times (like, opening a form, closing it, reopening it, etc), memory leaks. And big time.

I tried to look this up on Google but I didn't find anything good for a beginner.

  1. Is the garbage collector really freeing every objects when no longer used or there are exceptions that I have to handle? What am I missing?
  2. Are there free tools to look up for memory leaks?
+3  A: 

The GC will not necessarily actually free things at the moment the object is no longer referenced. The GC will collect it at some time in the future - you don't know exactly when, but if memory is needed, the GC will perform a collection if necessary.

Michael Burr
That's something that I found out. While looking at the task manager, after some times some memory were released. But, when starting the program it takes 20megs, go to 80megs and then drop to 60megs. That's still 40megs and i'm supposed to be in the same "state".
Procule
+8  A: 

What leads you to conclude that there are memory leaks? Under garbage collection, there is no guarantee that memory is freed immediately, and in general the GC doesn't kick in until your process memory reaches some threshold, or the available heap has been exhausted. (The exact heuristic is complicated and not important.) So the fact that your process's memory goes up and doesn't go down doesn't necessarily mean that there's a bug. It might just be that the GC didn't get around to cleaning up your process yet.

Additionally, are you sure that there are no references to your objects? It's possible that you have references that you aren't aware of. Most memory leaks in .NET applications are because people don't realize that their memory is still being referenced somewhere.

JSBangs
I understand your point. (see first comment to "Michael Burr"). But going from 20megs to 80megs... it seems a little high for me. Should I Dispose() the object manually ?
Procule
What do you want to `Dispose()`? Generally, most objects which implement the `IDisposable` interface should be disposed. I'm not sure about C++/CLR, but in C# that's often done with the `using` construct. However, in the case of form objects, life's a little bit different. And understand that the CLR itself has a moderately heavy footprint, and also that the memory report you see in Task Manager is quite possibly inaccurate. (Use ANTS or another good profiler to be certain that you really have a leak.)
John Rudy
Thanks, you seem to answer my second question. So "ANTS" is a program to help me manage/lookat the memory ?(btw, half my code is in C++/CLI and the other half in C# (I switched half way, it seems C# it's MUCH easier!))
Procule
I, personally, would agree that C# is easier. OTOH, I'm not much of a C++ developer. (In fact, I'd go so far as to say it's downright dangerous to put me on a C++ project.) ANTS is commercial software, but there is a 14-day trial available. http://www.red-gate.com/products/ants_performance_profiler/index.htm
John Rudy
Yeah I saw that. Is there any free alternative since it is just for learning ?
Procule
+9  A: 

Yeah, the garbage collector is freeing your objects when they're not used anymore.

What we usually call a memory leak in .NET is more like:

  • You're using external resources (that are not garbage collected) and forgetting to free them. This is solved usually by implementing the IDisposable interface.
  • You think there aren't references to a given object but indeed there are, somewhere and you're not using them any more but the garbage collector does not know about them.

In addition, memory is only reclaimed when needed, meaning the garbage collector activates at given times and performs a collection determining them what memory can be freed and freeing it. Until it does, the memory isn't claimed so it might look like memory is being lost.

Here, I think I'll provide a more complex answer just to clarify.

First, the garbage collector runs in its own thread. You have to understand that, in order do reclaim memory the garbage collector needs to stop all other threads so that he can follow up the reference chain an determine what depends on what. That's the reason memory isn't freed right away, a garbage collector imply certain costs in performance.

Internally the garbage collector manage memory in generations. In a very simplified way there are several generations for long lived, short lived and big size objects. The garbage collector moves the object from one generation to another each time its performs a collection which happens more often for short lived generation that for long lived one. It also reallocates objects to get you the most possible contiguous space so again, performing a collection is a costly process.

If you really want to see the effects of you freeing the form (when getting out of scope and having no more reference to it) you can call GC.Collect(). Do that just for testing, it's highly unwise to call Collect except for a very few cases where you know exactly what you're doing and the implications it will have.

A little more explaining about the Dispose method of the IDispose interface.

Dispose isn't a destructor in the usual C++ way, it isn't a destructor at all. Dispose is a way to deterministically get rid of unmanaged objects. An example: Suppose you call an external COM library that happens to allocate 1GB of memory due to what it is doing. If you have no Dispose that memory will sit there, wasting space until the GC inits a collection and reclaims the unmanaged memory by calling the actual object destructor. So if you want to free the memory right away you have to call the Dispose method but you're not "forced" to do so.

If you don't use IDisposable interface then you have to free you're unmanaged resources in the Finalize method. Finalize is automatically called from the object destructor when the GC attempts to reclaim the object. So if you have a proper finalize method the unmanaged memory will get freed either way. Calling Dispose will only make it deterministic.

Jorge Córdoba
(first button) Do you mean an unmanaged object ?(second button) You seem to say the same things of others. So, maybe the object is still referenced but by an object I don't know. Is there a way to know ? Because the debugger (in VS2008), only tells you about the locals. (I'm used to code in C/Linux)
Procule
@Jorge: +1, excellent explanation of the GC and generations. (Which is very helpful to someone just getting into .NET, and sadly my experience has shown one of the least-taught items.)
John Rudy
Yeah, I mean unmanaged objects in general but not always the obvious ones. For example an unsafe fixed pointer could make an object pinned and stop the reference object to being relocated which in turn could lead to memory fragmentation and in turn could lead to have a lot of "wasted space"
Jorge Córdoba
Ok. I understand pointers for sure. Is it the role of ~Function() to do that ? Do I always have to call the destructor and free() all the objects ?
Procule
Procule, I mean an error on your part where you have, for example, an object with a property that references another objects but you're not aware of it. The first object never falls out of scope and thus the second object is never released (imagine that with a very long reference chain). As said in other comments there're several profilers that discover those situations by pointing out what objects are taking the most memory so that you can decide if that's right or wrong
Jorge Córdoba
Procule, I've edited the answer to explain how IDisposable work and the difference between IDisposable and destructors.
Jorge Córdoba
Thanks Jorge. I understand. So if I don't have any unmanaged objects, I just wait for the GC to manage the memory ? For me it's just... akward. I'm used to free the structures when they are not used anymore. So if I understand you well, I'm doing right and even if I see the memory climbing in the Windows Task Manager, it doesn't mean anything about the real memory used. Is that right ?
Procule
Oh and btw, you should +1 me since I need 15 rep points to rep you ! hehe
Procule
A: 

There are free tools available to look at the managed heap in .Net, using the SOSEX extensions to WinDBG and SOS, it is possible to run a program, pause it, then look at which objects exist on the stack and (more importantly) which other objects are holding references to them (roots) which will be preventing the the GC from collecting them.

Colin Desmond
+3  A: 

I'm adding this as an answer rather than a comment on the question, but this follows-up a question asked by the OP in a comment: using statement on MSDN. IDisposable interface on MSDN.

Here is the crux of the issue: what you're used to as far as object destructors is gone. Everything you've been told about how to code correctly is screaming up from your subconscious, saying this can't be true, and if it is true, it's wrong and terrible. It's very different; it's hard to remember how much I really despised it at first (I was a proud C++ developer).

I personally promise you: it's going to be OK!

Here's another good thing to read: Destructors and Finalizers in Visual C++.

overslacked
lol I like how you tell that. I would +1 you but I need 15 points to do that. Thanks !
Procule
+7  A: 

Task Manager is a terrible way to examine your memory usage. If you want to study how the garbage collector works, install the CLR Profiler and use it to analyze your application. It will tell you exactly what the garbage collector is doing.

See How To: Use CLR Profiler.

Eric Lippert
Thanks for the advice, i'm looking into CLR Profiler. +1
Procule
I would go with perfmon first, before diving deeper with windbg or clr profiler, as it is much lighter weight and has a bunch of counters such as LOB heap size, Gen2 heaps size, collection counts etc... Also CLR profiler uses ICorProfiler which does not support attach so you need to make your decision to profile upfront
Sam Saffron
+1  A: 

If you just want to figure out if you have a memory leak or not, have a look at perfmon which ships with your copy of windows:

In particular the .NET CLR Memory counter bytes in all heaps, if this number is steadily growing you have a leak.

You can even dig deeper by comparing the Gen 2 heap size to the Large Object Heap Size. If the former is growing steadily you have a large blobs of data leaking.

Once you confirm there is a leak, you can attach with windbg and use the sos extensions to determine what is leaking.

If you can afford to spend a few bucks have a look at the .NET Memory Profiler.

Sam Saffron