Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ConcurrentQueue holds object's reference or value? "out of memory" exception

Is objects enqueued to a ConcurrentQueue are copied to the queue or just their references?

I don't understand any scenario.

Explanation:

I defined a ConcurrentQueue like this:

// BufferElement is a class I created
private ConcurrentQueue<BufferElement> _bufferQueue;

I have a function which is called a lot of times and it's purpsoe is to enqueue an element to the queue:

private void EnqueueElementToBuffer(string data, int moreData)
{
    // the bufferElement constructor is setting data and moreData to it's fields.
    BufferElement bufferElement = new BufferElement(data, moreData);
    bufferQueue.Enqueue(bufferElement);
}

When I run this I get Out of Memory exception after a while. I thought it might be because the garbage collector does not collect the bufferElement because it is still has referenced in the bufferQueue, so I changed the function to this:

private void EnqueueElementToBuffer(string data, int moreData)
{
    // _bufferElement is now a filed of the class
    _bufferElement.Data = data;
    _bufferElement.MoreData = moreData;
    bufferQueue.Enqueue(_bufferElement);
}

And I didn't get the exception, and wasn't about to get one judging by the memory in the windows task manager.

Now I thought that the problam was solved because when I enqueued objects to the queue only the reference to the objects is cpoied to the queue, but I was afraid that all the elements in the queue are referencing to the same object, so I checked another function I have in another thread which what it does is:

    // while bufferQueue is not empty do the following
    BufferElement bufferElement = null;
    bufferQueue.TryDequeue(out bufferElement);

And I checked the content of couple of elements and saw that their content was different! so if objects enqueued to the queue are copied by value why I got the Out of Memory exception at first?

like image 741
adl Avatar asked Apr 02 '12 18:04

adl


2 Answers

When you call Enqueue only a copy of the reference is stored in the ConcurrentQueue<T>. But this reference is strongly held which means it does keep the actual referred to object in memory. The element won't be eligible for collection until the reference is removed from the ConcurrentQueue<T>

The reason you didn't see an OutOfMemoryException when you switched to using a field is because you fundamentally changed the semantics

  • Original Code: Pushed N references to N elements into the queue hence it's holding N elements in memory
  • Changed Code: Pushed N references to 1 element into the queue hence it's holding 1 element in memory

This significantly reduced the amount of memory the ConcurrentQueue<T> object graph was holding in memory and prevented the exception.

It seems like the problem here is you are simply equeuing elements much faster than you are processing them. So long as this holds true you will eventually run out of memory in the application. Your code needs to be adjusted such that it won't hit this condition.

Note: This answer is written assuming BufferElement is a class and not a struct.

Note2: As Servy points out in the comments you may want to consider switching to a BlockingCollection<T> as it has a few throttling capabilities which may help you.

  • http://msdn.microsoft.com/en-us/library/dd267312.aspx
like image 182
JaredPar Avatar answered Nov 15 '22 06:11

JaredPar


To answer the first question, ConcurrentQueue<T> holds a reference to the content. This is true even for value type T, because those are boxed upon enqueueing.

The reason that you got an OOM to begin with is probably that you are either never dequeueing the items (and so yes, the queue will maintain references to them and keep them alive until you dequeue them) or you are not dequeueing them fast enough compared to how fast you are enqueueing them.

In your "fix", you simply only ever create the BufferElement once, and continually overwrite it's fields. You enqueue the same class instance over and over again, which is almost certainly not the behavior that you want.

like image 23
Chris Shain Avatar answered Nov 15 '22 07:11

Chris Shain