ConcurrentQueue保存对象';的参考或价值";内存不足”;例外

本文关键字:quot 例外 内存不足 参考 对象 保存 ConcurrentQueue | 更新日期: 2023-09-27 18:29:57

是将排入ConcurrentQueue的对象复制到队列中,还是仅复制它们的引用?

我不了解任何情况。

说明:

我定义了一个ConcurrentQueue,如下所示:

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

我有一个函数被调用了很多次,它的目的是将一个元素排入队列:

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

当我运行这个程序时,过一段时间就会出现内存不足异常。我想这可能是因为垃圾收集器没有收集bufferElement,因为它仍然在bufferQueue中被引用,所以我将函数改为:

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

根据windows任务管理器中的内存判断,我没有得到异常,也不打算得到异常。

现在我认为问题已经解决了,因为当我将对象排入队列时,只有对对象的引用才会被写入队列,但我担心队列中的所有元素都引用了同一个对象,所以我检查了另一个线程中的另一个函数,它的作用是:

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

我检查了几个元素的内容,发现它们的内容不同!那么,如果排队到队列的对象是按值复制的,为什么我一开始会出现内存不足异常?

ConcurrentQueue保存对象';的参考或价值";内存不足”;例外

调用Enqueue时,只有引用的副本存储在ConcurrentQueue<T>中。但是这个引用是强保存的,这意味着它确实将实际引用的对象保存在内存中。在从ConcurrentQueue<T> 中删除引用之前,元素将不符合收集条件

当您切换到使用字段时,您没有看到OutOfMemoryException的原因是因为您从根本上改变了语义

  • 原始代码:将对N个元素的N个引用推入队列,因此它在内存中包含N个元素
  • 更改的代码:将对1个元素的N个引用推送到队列中,因此它在内存中包含1个元素

这显著减少了ConcurrentQueue<T>对象图在内存中的内存量,并防止了异常。

这里的问题似乎是,你只是以比处理它们快得多的速度来配置元素。只要这一点成立,您最终就会耗尽应用程序中的内存。您的代码需要进行调整,使其不会达到此条件。

注意:这个答案是在假设BufferElementclass而不是struct的情况下编写的。

注2:正如Servy在评论中指出的那样,您可能需要考虑切换到BlockingCollection<T>,因为它具有一些节流功能,这可能会对您有所帮助。

  • http://msdn.microsoft.com/en-us/library/dd267312.aspx

要回答第一个问题,ConcurrentQueue<T>保存对内容的引用。即使对于值类型T也是如此,因为这些值在排队时被装箱。

您获得OOM的原因可能是您从未将项目排到队列之外(因此,是的,队列将保留对它们的引用并保持它们的活动状态,直到您将它们排到队列以外),或者与您将它们排队的速度相比,您排到队列外的速度不够快。

在"修复"中,您只需创建BufferElement一次,并不断覆盖其字段。您一次又一次地将同一个类实例排队,这几乎肯定不是您想要的行为。