5

I have a class that has a byte array holding from 1,048,576 bytes up to 134,217,728 bytes. In the dispose void I have the array set to null and the method calling the dispose calls GC.Collect after that.

If I dispose right away I will get my memory back, but if I wait like 10 hours and dispose, memory usage doesn't change.

jasonbay13
  • 51
  • 1
  • 1
  • 2
  • 14
    So far so good. And what is your question? – Darin Dimitrov Nov 28 '11 at 16:49
  • i dont understand why time would cause the memory to not be freed. and if there is a solution to the problem. – jasonbay13 Nov 28 '11 at 16:51
  • 6
    @jasonbay13: When memory is used that long, it gets promoted down the generations of garbage collection. When it's in later generations, it tends not to get collected until the system needs it, it's not a leak, per se. Have you tried the `Force` option on `GC.Collect` and specify the generations to collect? – James Michael Hare Nov 28 '11 at 16:54
  • @jasonbay13: p.s. I don't really recommend forcing GC, let the GC do it's thing. You're holding onto the memory for a long time, so the GC assumes you will continue to do so and checks it much less frequently. Look up generational garbage collection for more details. – James Michael Hare Nov 28 '11 at 16:55
  • @jasonbay13: For more info on generational GC in .NET, see http://msdn.microsoft.com/en-us/library/ms973837.aspx – James Michael Hare Nov 28 '11 at 16:56
  • Except if memory is ***REALLY*** not freed (You checked with a memory profiler, NOT the task manager) just let the GC and OS do their work and don't stress on the values that the task manager display. – Julien Roncaglia Nov 28 '11 at 16:57
  • James Michael Hare has good explanation. I would also ask if you are sure that it is the same memory after 10 hours? Maybe your software disposed it and allocated sth new. – Piotr Perak Nov 28 '11 at 16:59
  • 4
    You haven't explained why this is a problem. Memory isn't freed right away. OK, so who cares? It will be freed when it needs to be freed. Let the garbage collector do its job. – Eric Lippert Nov 28 '11 at 17:07

3 Answers3

7

Memory usage is based on OS memory allocation. It may be freed immediately, it may not be. This depends on OS utilization, application, etc. You free it in the runtime, but this doesn't mean the OS alway gets it back. I have to think heres a determination based on memory patterns (ie time here) that affects this calculation of when to return it or not. This was addressed here:

Explicitly freeing memory in c#

Community
  • 1
  • 1
Adam Tuliper
  • 29,982
  • 4
  • 53
  • 71
1

Note: if the memory isn't released, it doesn't automatically means it used. It's up to the CLR whether it's releases chuncks of memory or not, but this memory isn't wasted.

That said, if you want a technical explanation, you'll want to read litterature about the Large Object Heap: http://msdn.microsoft.com/en-us/magazine/cc534993.aspx

Basically, it's a zone of memory where very large objects (more than 85kB) are allocated. It differs from the other zones of memory in that it's never compacted, and thus can become fragmented. I think what happens in your case is:

  • Case 1: you allocate the object and immediately call GC.Collect. The object is allocated at the end of the heap, then freed. The CLR sees a free segment at the end of the heap and releases it to the OS.

  • Case 2: you allocate the object and wait for a while. In the mean time, an other object is allocated in the LOH. Now, your object isn't the last one anymore. Then, when you call GC.Collect, your object is erased, but there's still the other object(s) at the end of the memory segment. So the CLR cannot release the memory to the OS.

Just a guess based on my knowledge of memory management in .NET. I may be completely wrong.

Kevin Gosse
  • 38,392
  • 3
  • 78
  • 94
1

Your findings are not unusual, but it doesn't mean that anything is wrong either. In order to be collected, something must prompt the GC to collect (often an attempted allocation). As a result, you can build an app that consumes a bunch of memory, releases it, and then goes idle. If there is no memory pressure on the machine, and if your app doesn't try to do anything after that, the GC won't fire (because it doesn't need to). Once you get busy, the GC will kick in and do its job. This behavior is very commonly mistaken for a leak.

BTW: Are you using that very large array more than once? If so, you might be better off keeping it around and reusing it. Reason: any object larger than 85,000 bytes is allocated on the Large Object Heap. That heap only gets GC'd on Generation 2 collections. So if you are allocating and reallocating arrays very often, you will be causing a lot of Gen 2 (expensive) collections.

(note: that doesn't mean that there's a hard and fast rule to always reuse large arrays, but if you are doing a lot of allocation/deallocation/allocation of the array, you should measure how much it helps if you re-use).

JMarsch
  • 21,484
  • 15
  • 77
  • 125
  • actually i make instances of the class containing different size arrays, and at random any one could be disposed of. – jasonbay13 Nov 28 '11 at 18:29
  • I see. If you want to really test whether a leak is occuring, you could throw in some test code that just keeps creating and releasing those arrays indefinitely. They will eventually go away. Probably what's happening is that there isn't enough pressure on the system to prompt a Generation 2 collection. Or, it could be as Adam Tuliper says -- the array is being freed, but the memory is not going back to the O/S immediately. Either way, so long as you aren't holding a reference to the array somewhere, it probably is not a real leak. – JMarsch Nov 28 '11 at 18:55