簡體   English   中英

使用帶有終結器的C ++ / CLI定義的類時,C#中的內存泄漏

[英]Memory leaks in C# while using C++/CLI defined class with finalizer

當我在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中實現終結器時,就會發生這種內存泄漏(或垃圾收集的糟糕工作)。

為什么會發生內存泄漏? 我怎么能避免它,並仍然可以使用終結器進行其他(更復雜)的使用?


更新:原因當然不是寫入控制台/標准輸出或終結器中的其他非標准代碼,以下類具有相同的內存泄漏行為:

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

當您分配的速度超過垃圾收集時,您將遇到OOM。 如果你進行大量分配,CLR會插入一個Sleep(xx)進行限制分配,但在極端情況下這還不夠。

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

我還沒有測量它,但我認為即使是一個空的終結器也會導致這個問題,因為增加的對象生命周期和兩個終結隊列處理(終結器隊列和f可達隊列)確實會產生足夠的開銷來使終結比分配慢。

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

因此,您的OOM是預期的。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM