Memory leak using ConcurrentQueue

I have a memory leak when using ConcurrentQueue :

 requestObject request = xxx; Item obj= new Item (); obj.MessageReceived += obj_MessageReceived; obj.Exited += obj_Exited; request.Key = obj.Key; obj.AddRequest(request); _queue.TryAdd(obj.Key, obj); 

In the Exit callback, I delete the resource:

 void LiveSphere_Exited(string key) { Item instance; _queue.TryRemove(key, out instance); Task.Factory.StartNew(() => { var wait = new SpinWait(); while (instance.MessageCount > 0) { wait.SpinOnce(); } }) .ContinueWith((t) => { if (instance != null) { //Cleanup resources instance.MessageReceived -= obj_MessageReceived; instance.Exited -= obj_Exited; instance.Dispose(); instance = null; } }); } 

When I look at the code, I still have an β€œItem” object associated with the root, but I don’t know where I can dispose ..., The exited method is launched, and _queue removes the β€œItem” object from the queue.

When I read the documentation, concurrentqueue copies the link to the queue.

Can you help me find out where the memory leak is?

+7
source share
2 answers

Unlike the standard .NET queue, calling Dequeue () does not remove the object reference from the collection. Although this behavior has changed from version 4.0 to version 4.5 (I read this, but have not tested it), this is not a mistake, but a conscious design decision made by the framework team as part of the development of a thread-safe, enumerable collection.

This article has more information, including a workaround using StrongBox to wrap objects that are part of ConcurrentQueue. This should be a good workaround until you go to box 4.5.

+6
source

I reviewed the implementation of a parallel queue. There are times when the queue will contain references to the object after calling Dequeue ().

A parallel queue uses segments to store data. There, it is part of the TryRemove method of the segment:

 // If there is no other thread taking snapshot (GetEnumerator(), ToList(), etc), reset the deleted entry to null. // It is ok if after this conditional check m_numSnapshotTakers becomes > 0, because new snapshots won't include // the deleted entry at m_array[lowLocal]. if (m_source.m_numSnapshotTakers <= 0) { m_array[lowLocal] = default(T); //release the reference to the object. } 

So, when you have another thread that enumerates the queue at the same time, you delete the object whose object references will not be set to zero.

+1
source

All Articles