C# ConcurrentQueue保存对象';什么是参考价值&引用;“内存不足”;例外

C# ConcurrentQueue保存对象';什么是参考价值&引用;“内存不足”;例外,c#,memory,concurrency,queue,C#,Memory,Concurrency,Queue,列队到ConcurrentQueue的对象是复制到队列还是仅复制到它们的引用 我不理解任何情况 说明: 我定义了一个ConcurrentQueue,如下所示: // BufferElement is a class I created private ConcurrentQueue<BufferElement> _bufferQueue; 当我运行这个程序时,我会在一段时间后出现内存不足异常。我认为这可能是因为垃圾收集器没有收集bufferElement,因为它仍然在bufferQ

列队到ConcurrentQueue的对象是复制到队列还是仅复制到它们的引用

我不理解任何情况

说明:

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

// BufferElement is a class I created
private ConcurrentQueue<BufferElement> _bufferQueue;
当我运行这个程序时,我会在一段时间后出现内存不足异常。我认为这可能是因为垃圾收集器没有收集
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);

我检查了两个元素的内容,发现它们的内容不同!因此,如果队列中的对象是按值复制的,那么为什么我首先会出现内存不足异常?

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

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

  • 原始代码:将N个对N个元素的引用推送到队列中,因此它在内存中保存了N个元素
  • 更改代码:将对1个元素的N个引用推送到队列中,因此它在内存中保留1个元素
这大大减少了
ConcurrentQueue
对象图在内存中的内存量,并防止了异常

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

注意:这个答案是假设
BufferElement
类而不是
结构编写的

<2>注释2:正如Servy在评论中指出的,你可能想考虑切换到<代码> BuffjCopys<代码>,因为它有一些节流功能,可以帮助你。


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

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


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

答案很大程度上取决于
BufferElement
是什么,是类还是结构。您可以使用
BlockingCollection
(内部用作并发队列)并将其容量设置为,当您尝试添加超过X个项目时,它将阻塞,直到有可用空间为止,这可能是您需要减慢生产者的速度并使其与消费者保持一致的原因。将值类型T装箱是不正确的-它们将被复制到
ConcurrentQueue.Segment.m_数组中,但不会被装箱。
    // while bufferQueue is not empty do the following
    BufferElement bufferElement = null;
    bufferQueue.TryDequeue(out bufferElement);