English 中文(简体)
Question about the garbage collector in .NET (memory leak)
原标题:

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?
最佳回答

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.

问题回答

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.

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.

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++.

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.

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.

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.





相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签