views:

86

answers:

2

Monitoring my program's Virtual Bytes usage while it is running showed that by doing some kind operations, the virtual bytes usage goes up by about 1GB in about 5 minutes. The program deals with tcp sockets and high data transfer throughput between them (~800Mbps).

Loading a dump file of the program in windbg showed that the reason for the very high and fast memory usage is about 1GB of "free" objects. Indeed, when I call the garbage collector (gen 0, 1 & 2) from the console screen of the program (after getting it to this state) it frees up about 1GB of memory usage.

I'm trying to understand what exactly are these free objects and why aren't they garbage collected by the garbage collector automatically.

Edit: One suggestion was that I may be creating the objects in the Large Object Heap and it becomes fragmanted but this is not the case as I've seen that all of the "free" objects sits in Gen 2 Heap.

Other suggestion was that maybe Gen 2 Heap gets fragmented because of pinned objects but if this was the case, GC.Collect wouldn't fix the problem but it actually does so I believe this is not the case as well.

What I suspect from the discussion with Paul is that the memory does gets freed but is from some reason returned to the OS rarely or only when I manually call GC.Collect.

+1  A: 

They are not free 'objects', they are free space. .NET does not release memory it has used back to the operating system immediately. Any free blocks can be used for subsequent object allocations, providing they fit inside the free block (otherwise the heap has to be extended by asking the operating system to allocate more memory).

The garbage collector makes efforts to combine free space into large usable blocks by compacting generation 2. This is not always possible: for example an application may pin objects which will potentially prevent the garbage collector from combining free space by moving the live objects to the front of the heap. If this happens a lot, the application's memory will be broken up into useless small blocks and this affect is known as 'heap fragmentation'.

In addition, there is a Large Object Heap (LOH) in which larger objects are allocated. The rationale is that there is a cost associated with heap compaction as data must be copied around and so the LOH is not compacted, avoiding these costs. However, the flipside is that the LOH can become easily fragmented, with small, less useful blocks of free memory interspersed between live objects.

I would suggest running a dumpheap -stat. This command will report at the end of the list any fragmented blocks. You can then dump those blocks to get an idea of what is happening.

Paul Ruane
Thanks for the answer.Examining the heaps sizes showed that Gen 2 is the one taking up all the memory and indeed calling GC.Collect(0) or GC.Collect(1) hasn't freed the memory while GC.Collect(2) has done the job.# of Pinned Objects remains low during all of the exceution.When I monitor # Gen 2 Collections in perfmon I see that it increases consistently but the memory from Gen2 very rarely gets freed automatically.It does get freed when I call GC.Collect(2) though, as mentioned above.What could possibly exaplin this?
galbarm
@galbarm: Sounds like it's just the strategy the .NET CLR takes to managment of memory. Remember the memory that .NET uses for the managed heaps must be allocated by the operating system and this operation obviously has an overhead. It would make sense for it to hang on to memory rather than continuously allocate and release blocks of memory. As an experiment you could try creating a second process that gobbles memory and see if a low system memory events triggers your existing app into releasing the free memory it has available.
Paul Ruane
How is it possible that I see constant increase in # Gen 2 Collections but the memory is not freed until I manualy call GC.Collect?
galbarm
@galbarm: A .NET heap collection will free memory *within the heap*, but .NET will not necessarily return that memory to the operating system, thus if you look at a tool like Task Manager it may appear that your process is using lots of memory but that does not mean that it is necessarily being used to store anything. Imagine you take 100 sheets of paper from a store room but write on only 10. You will appear to be consuming paper but you are not actually making use of it. You may delay returing the paper to the store room, where others can use it, until someone moans about the lack of paper.
Paul Ruane
I understand.Is there a way forcing .NET to return the memory back to the OS or manipulating it to do so?I'm in a serious trouble here because I reach very high amount of memory (up to OutOfMemory exceptions) which is free and from some reason .NET doesn't return this memory.BTW, I belive that GC.Collect works a little different from regular automatic GC calls, by forcing returning the memory back to the OS. (At least that's what I saw in my tests)
galbarm
@galbarm: If you are looking at the memory dump *after* the exception then the stack would have already been unwound and the garbage collection already performed, thus you may be looking at the post-disaster situation rather than the causation. Instead you will need to look at the memory dump (or attach the debugger) whilst memory is high but before the exception. Also, if your process is maxing out the CPU, this may affect whether memory is returned or not.
Paul Ruane
I'm taking the dump before the exception but all the memory I see is of type "Free" when I run dumpheap -stat in windbg.
galbarm
+1  A: 

By the way, it looks like you have a well-known problem (at least among socket gurus) that most socket servers get in .Net. Paul has already touched on what it means. To elaborate more, what is going wrong is that during a Read/Write on the socket the buffer gets pinned - which means the GC isn't allowed to move it around (as such your heap fragments). Coversant (who pioneered the solution) were seeing an OutOfMemoryException when their actual memory usage was only about 500MB (due to such heavy fragmentation). Fixing it is another story entirely.

What you want to do is at application start up allocate a very large amount of buffers (I am currently doing 50MB). You will find the new ArraySegment<T> (v2.0) and ConcurrentQueue<T> (v4.0) classes particularly useful when writing this. There are a few lock-free queues floating around on the tubes if you are not using v4.0 yet.

// Pseudo-code.
ArraySegment<byte> CheckOut()
{
  ArraySegment<byte> result;
  while(!_queue.TryDequeue(out result))
    GrowBufferQueue(); //Enqueue a bunch more buffers.
  return result;
}

void CheckOut(ArraySegment<byte> buffer)
{
  _queue.Enqueue(buffer);
}

void GrowBufferQueue()
{
  // Verify this, I did throw it together in 30s.
  // Allocates nearly 2MB. You might want to tweak that.
  for(var j = 0; j < 5; j++)
  {
    var buffer = new byte[409600]; // 4096 = page size on Windows.
    for(var i = 0; i < 409600; i += 4096)
      _queue.Enqueue(new ArraySegment<byte>(buffer, i, 4096));
  }
}

Following this you will need to subclass NetworkStream and swap out the incoming buffer with one from your buffer pool. Buffer::BlockCopy will help performance (don't use Array::Copy). This is complicated and hairy; especially if you make it async-capable.

If you are not layering streams (e.g. SSLStream <--> DeflateStream <--> XmlWriter etc.) you should use the new socket async pattern in .Net 4.0; which has more efficiency around the IAsyncResults that get passed around. Because you are not layering streams you have full control over the buffers that get used - so you don't need to go the NetworkStream subclass route.

Jonathan Dickinson