内存泄漏与ConcurrentQueue(Memory leak with ConcurrentQu

2019-08-04 17:20发布

我使用时有内存泄漏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);

在“已退出”的回调,我处置资源:

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;
         }
    });
}

当我分析代码,我仍然有一个根目录的“项目”对象,但我不知道在哪里可以处理......,被触发的退出方法和_queue已删除队列中的“项目”对象。

当我阅读文档时,concurrentqueue复制参考到队列中。

你能帮我找出内存泄漏是什么?

Answer 1:

不同于标准的.NET队列,调用出列()不删除参考从集合中的对象。 虽然这种行为已经从4.0版本改为4.5版本(我看了这一点,但还没有测试),它是不是一个错误,而是由框架队作出设计的一部分,有意识的设计决策线程安全,枚举集合。

这篇文章有更多的信息,包括使用的StrongBox来包装进入ConcurrentQueue对象解决方法。 这应该是一个合适的工作,各地,直到你可以移动到4.5框架。



Answer 2:

我已经看过实施并发队列中。 有些时候出列后,队列将持有对象的引用的情况下()被调用。

并发队列使用段来存储数据。 在那里,它是该段的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. 
} 

所以,当你有一个不同的线程枚举在同一时间,你出列的对象的对象引用不会被设置为空队列。



文章来源: Memory leak with ConcurrentQueue