繁体   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