2

I found out a weird thing in GC, when I have application that removed reference to many large objects, it still eats a lot of ram, in my case around 300mb. This memory is not cleaned even if I call GC.Collect()

Then I tried to randomly call it 10 times in few seconds, and result: 200mb of ram was freed resulting in my application eating ONLY 100mb instead of 300mb of private ram (not virtual but the physical / resident ram)

Why is this? How can I make GC clean this memory automatically without having to call it by hand?

Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
Petr
  • 13,747
  • 20
  • 89
  • 144
  • How large are your objects? More than 85,000 bytes? If so, they are going on the Large Object Heap. The GC has different rules about the LOH than it does other memory. If they are on the LOH, you might be looking at memory fragmentation. – SaulBack Apr 18 '13 at 18:36
  • You can use sos.dll http://msdn.microsoft.com/en-us/library/bb190764.aspx to see what objects are alive in memory and what is holding a reference to them. That may help narrow down what's causing your observation – Pete Baughman Apr 18 '13 at 18:36
  • I can't use sos because I have free version of Visual Studio, however my objects are usually not so large, most of them, some might be – Petr Apr 18 '13 at 18:42

3 Answers3

4

Either:

  1. You are still holding onto a reference to the large object somewhere, so even calling GC.Collect can't clean it up.

  2. There is unmanaged memory that accounts for the majority of this memory consumed. You are not properly disposing of that unmanaged resource.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • yes this very likely may be the 2 because I am using some 3rd c++ library that is likely causing this delay in memory freeing... or I don't know, but it seems to me that actually calling of GC.Collect MULTIPLE TIMES was leading to this. And that is weird – Petr Apr 18 '13 at 18:35
  • maybe is it possible that some object that there is no reference to, contains a reference to another and the same for say 10 other objects in a chain.. so in every run of GC.Collect one of them get removed and another reference get cleaned? – Petr Apr 18 '13 at 18:36
  • 2
    @Petr That's probably because the collection is causing a finalizer to run, which will eventually result in the unmanaged memory being freed. If you waited for the collection to happen once, and then waited even longer for the finalizer queue to run, you'd see the same thing. The "proper" solution is to manually dispose of the `IDisposable` object instead of leaving it for the finalizer. – Servy Apr 18 '13 at 18:37
  • @Petr To your second comment, no. If there were an entire graph of objects referencing each other, but none of them were referenced by a rooted object then they would all be cleaned up. Calling multiple collections isn't doing anything for you; it's calling one and then just waiting for a period of time. What you do in that time isn't relevant. – Servy Apr 18 '13 at 18:38
0

The Garbage Collector will collect any unused objects whenever more memory is needed. An object is only unused when you have no references to it. Use a memory profiler to see what objects are eating up your memory and why they are not collected.

Other than that, you should not need to call GC.Collect ever. The Garbage Collector is more than capable and optimized to choose when it will execute.

Daniel A.A. Pelsmaeker
  • 47,471
  • 20
  • 111
  • 157
  • that is what I would like to do: not call it. But for some reason without actually calling it - the memory doesn't get freed :/ and that is problem here – Petr Apr 18 '13 at 18:37
  • Why do you need that memory to get freed at that time? The GC will already free it automatically if the memory is needed for something else, otherwise, it will take its time. – Mason Apr 18 '13 at 19:16
  • GC has no idea about the other applications, so it can't know if system could need that memory or not, or if other applications would – Petr Apr 18 '13 at 21:20
  • @Petr Managed objects will be collected automatically by the GC. Unmanaged objects will never be collected by the GC, no matter how many times you call `GC.Collect`. You'll have to free them manually, for example by implementing the [`IDisposable` pattern](http://stackoverflow.com/a/538238/146622). – Daniel A.A. Pelsmaeker Apr 18 '13 at 23:32
0

Lots of GC questions on SO today!

Maybe it was going to clean it up after that much time had passed anyway. If your program runs out of memory and dies without having that extra call, leave it in, otherwise it's probably unnecessary.

djdanlib
  • 21,449
  • 1
  • 20
  • 29
  • well, but there are other programs too on this machine and they want some memory :) even if there is enough memory for programs... why should I swap out just because GC.Collect didn't decide to run yet :| I would prefer it to run ANYTIME there is something to clear rather than only when system is running OOM – Petr Apr 18 '13 at 18:41
  • To answer your question - it's designed that way for a lot of reasons. Those other programs also want CPU cycles, and the GC could be working on some other program too. The GC correctly defers to allow other programs some CPU time before it thrashes about and cleans house. Memory pressure is a difficult beast... I suggest reading up on how the .NET garbage collector works, so you can know what actually goes on under the hood. Understanding the platform will make your code better in the long run. You did not indicate that your program was sent to swap, in fact it should not be the case. – djdanlib Apr 18 '13 at 20:55