2013-03-20 43 views
4

C++/CLI定義的類當我實現在C++/CLI DLL的類:內存泄漏,同時使用與終結

public ref class DummyClass 
{ 
protected: 
    !DummyClass() 
    { 
     // some dummy code: 
     std::cout << "hello" << std::endl; 
    } 
} 

,當我加載DLL到C#項目和通過重複地只使用類創建一個對象:

static void Main() 
{ 
    while (true) 
    { 
     var obj = new DummyClass(); 
    } 
} 

然後,在運行程序時,內存被緩慢地消化爲OutOfMemoryException。

看來,這種內存泄漏(或垃圾收集的糟糕工作)每次在C++/CLI中實現終結器時都會發生。

爲什麼會發生這種內存泄漏?我怎麼能避免它,並仍然能夠使用終結器的一些其他(更復雜)的使用?


UPDATE:原因肯定不是以書面控制檯/標準輸出或在終結其他非標準代碼,下面的類具有相同的存儲器泄漏行爲:

public ref class DummyClass 
{ 
private: 
    double * ptr; 
public: 
    DummyClass() 
    { 
     ptr = new double[5]; 
    } 
protected: 
    !DummyClass() 
    { 
     delete [] ptr; 
    } 
} 
+3

寫入'Console' /'stdout'會導致它。請參閱http://xacc.wordpress.com/2011/02/22/gc-suppressfinalize/以獲取我的觀察結果和一些評論。 .NET類中的一般規則在終結器中不做任何事情(除了正確實現「IDisposable」)。 – leppie 2013-03-20 11:10:54

+0

我更新了這個問題,所以你可以看到,即使你在終結器中做了你應該做的事情(例如,刪除非託管資源),你仍然會得到相同的內存泄漏結果。 – frakon 2013-03-20 11:49:01

+0

對此不太確定,但我想我記得C++/CLI有一個終結器和一個析構函數(事實上''DummyClass'爲析構函數)。改爲嘗試。 – leppie 2013-03-20 12:06:08

回答

3

當你分配的速度比你可以進入OOM的垃圾收集速度快。如果您執行大量分配,CLR將插入Sleep(xx)來節流分配,但在極端情況下這還不夠。

當你實現一個終結器時,你的對象被添加到終止隊列中,當它被終結時,它將從隊列中被移除。這會增加額外的開銷,並且會使對象的使用壽命超過必要的時間。即使您的對象在廉價的Gen 0 GC期間可以被釋放,它仍然會被最終化隊列引用。當完整的GC發生時,CLR確實會觸發最終化線程開始清理。這沒有幫助,因爲你的分配速度比你最終確定的速度快(寫入標準輸出速度非常慢),你的定型隊列將變得越來越大,從而導致終結時間越來越慢。

我還沒有測量它,但我認爲即使是空的終結器也會導致此問題,因爲增加的對象生存期和兩個終結隊列處理(終結器隊列和f-reachable隊列)確實會造成足夠的開銷,使終結比分配慢。

您需要記住,終結是一種固有的異步操作,在特定的時間點沒有執行保證。在允許額外分配之前,CLR將永遠不會等待清理所有掛起的終結器。如果你在10個線程上分配,那麼你之後仍然會有一個終結器線程清理。如果你想依靠確定性終結,你需要等待調用GC.WaitForPendingFinalizers() ,但這會使你的性能停滯不前。

因此預計您的OOM。

+0

謝謝!當我在創建每個1000000th DummyObject(因此GC有它的時間)之後插入'Thread.Sleep(1000);'時,內存停止泄漏! – frakon 2013-03-20 12:27:30

+0

建議'GC.WaitForPendingFinalizers()'每X次迭代一次也解決了這個問題。謝謝。 – frakon 2013-03-20 12:42:20

+0

這不是GC花費時間,它是終結者線程的延長時間,這是限制你。如果你能使終結器運行得更快,你需要停止更少的頻率。 – 2013-03-20 13:33:58

0

您應該使用AddMemoryPressure函數,否則垃圾收集器會低估需要及時清理這些對象。