2012-04-02 52 views
3

是否將已排入ConcurrentQueue的對象複製到隊列或僅引用它們?ConcurrentQueue是否保存對象的引用或值? 「內存不足」異常

我不明白任何情況。

說明:

我定義了一個ConcurrentQueue這樣的:

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

我有一個被稱爲很多次函數,它的purpsoe是一個元素排隊隊列:

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

而且我沒有得到的異常,並WASN將不會通過Windows任務管理器中的內存進行判斷。

現在我認爲problam已經解決了,因爲當我將對象排入隊列時,只有對對象的引用被複制到隊列中,但是我擔心隊列中的所有元素都引用同一個對象,所以我檢查其他功能我在另一個線程其中它的作用是:

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

而且我查了幾個元素的含量,發現他們的內容是不同的!所以如果隊列中的對象被值複製,爲什麼最初我得到了內存不足異常?

+0

答案在很大程度上取決於什麼'BufferElement'是一個類或一個結構。 – 2012-04-02 18:28:27

+0

'BufferElement'是一個類 – remi 2012-04-02 18:36:18

回答

3

當您致電Enqueue時,只有參考文獻的副本存儲在ConcurrentQueue<T>中。但是這個引用是強烈的,這意味着它確實將實際引用的對象保留在內存中。直到參考從ConcurrentQueue<T>

刪除您沒有看到OutOfMemoryException當你切換到使用現場的原因是該元素將不符合回收,因爲你根本上改變了語義

  • 原始編碼:推ñ引用N個元素到隊列因此它保持在存儲器中的N個元素
  • 改變的代碼:推ñ引用1個元件到隊列因此它保持1個元件在存儲器

這大大減少了對象圖在內存中佔用的內存量並防止了異常。

看來這裏的問題看起來就像是你處理元素的速度比你處理元素快得多。只要這種情況成立,你最終會在應用程序中耗盡內存。您的代碼需要進行調整,使其不會遇到這種情況。

注意:這個答案是假設BufferElementclass而不是struct。注2:正如Servy在評論中指出的那樣,您可能需要考慮切換爲BlockingCollection<T>,因爲它有一些可能對您有幫助的調節功能。

+2

您可以使用'BlockingCollection'(內部用作併發隊列)並設置其容量,以便當您嘗試添加多於X個項目時,它將阻塞,直到有空閒空間爲止你需要放慢你的生產者,並讓他們與消費者保持一致。 – Servy 2012-04-02 18:30:28

1

要回答第一個問題,ConcurrentQueue<T>持有的內容的引用。即使對於值類型T也是如此,因爲這些值在排隊時被裝箱。

你得到一個OOM開始的原因可能是你永遠不會將這些項目出隊(並且是的,隊列將保持對它們的引用,並保持它們一直存在,直到你將它們出隊)或者你沒有去隊列它們的速度足夠快,比你排隊的速度快。

在您的「修復」中,您只需創建一次BufferElement,並不斷覆蓋它的字段。你一遍又一遍地排隊同一個類實例,這幾乎肯定不是你想要的行爲。

+0

值類型T會被裝箱是不正確的 - 它們會被複制到'ConcurrentQueue .Segment.m_array'中,但不會被裝箱。 – crimbo 2015-02-14 18:48:44