ConcurrentQueue导致内存泄漏

dnx*_*dnx 8 c# memory-leaks

使用时我有内存泄漏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);
Run Code Online (Sandbox Code Playgroud)

在"已退出"回调中,我处置资源:

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;
         }
    });
}
Run Code Online (Sandbox Code Playgroud)

当我分析代码时,我仍然有一个根引用的"Item"对象,但我不知道我可以在哪里处理......,退出的方法被触发,_queue已从队列中删除了"Item"对象.

当我阅读文档时,concurrentqueue将引用复制到队列中.

你能帮我找出内存泄漏的位置吗?

rsg*_*een 6

与标准.NET队列不同,调用Dequeue()不会从集合中删除对象的引用.虽然这种行为已经从4.0版本变为4.5版本(我已经读过这篇文章,但还没有测试过),但这不是一个错误,而是框架团队作为设计线程安全的一部分做出的有意识的设计决策,可枚举的集合.

本文提供了更多信息,包括使用StrongBox来包装进入ConcurrentQueue的对象的变通方法.这应该是一个合适的解决方法,直到你可以转到4.5框架.

  • 考虑到它的结果,我称之为"有意识的设计错误". (2认同)
  • 我已经尝试过这种 StrongBox 方法,但只会延迟问题。内存仍然增加到大约 1.7GIG,然后应用程序出现延迟问题和崩溃。 (2认同)

小智 5

我查看了并发队列的实现。在某些情况下,调用 Dequeue() 后队列将保存对对象的引用。

并发队列使用Segment来存储数据。它是段的 TryRemove 方法的一部分:

// 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. 
} 
Run Code Online (Sandbox Code Playgroud)

因此,当您有一个不同的线程枚举队列的同时,您将一个对象出列时,对该对象的引用将不会被设置为 null。