简体   繁体   English

使用带有终结器的C ++ / CLI定义的类时,C#中的内存泄漏

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

When I implement a class in C++/CLI DLL: 当我在C ++ / CLI DLL中实现一个类时:

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

and when I load that DLL to C# project and use the class just by repeatedly creating an object: 当我将该DLL加载到C#项目并通过重复创建一个对象来使用该类时:

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

then, while running the program, memory is slowly digested to OutOfMemoryException. 然后,在运行程序时,内存被慢慢消化为OutOfMemoryException。

It seems, that this memory leak (or bad work of garbage collection) happens everytime I implement finalizer in C++/CLI. 看来,每当我在C ++ / CLI中实现终结器时,就会发生这种内存泄漏(或垃圾收集的糟糕工作)。

Why happens this memory leak? 为什么会发生内存泄漏? How could I avoid it and be still able to use finalizer for some other (more complicated) use? 我怎么能避免它,并仍然可以使用终结器进行其他(更复杂)的使用?


UPDATE: The cause surely is not in writing to Console / stdout or other non-standard code in finalizer, the following class has the same memory leaking behaviour: 更新:原因当然不是写入控制台/标准输出或终结器中的其他非标准代码,以下类具有相同的内存泄漏行为:

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

When you allocate faster than you can garbage collect you will run into OOM. 当您分配的速度超过垃圾收集时,您将遇到OOM。 If you do heavy allocations the CLR will insert a Sleep(xx) to throttle allocation but this is not enough in your extreme case. 如果你进行大量分配,CLR会插入一个Sleep(xx)进行限制分配,但在极端情况下这还不够。

When you implement a finalizer your object is added to the finalization queue and when it was finalized it is removed from the queue. 实现终结器时,您的对象将添加到终结队列中,并在完成时将其从队列中删除。 This does impose additional overhead and you will make your object life longer than necessary. 这会产生额外的开销,您将使对象的寿命超过必要的时间。 Even if your object could be freed during a cheap Gen 0 GC it is still referenced by the finalization queue. 即使你的对象可以在廉价的Gen 0 GC中被释放,它仍然被终结队列引用。 When a full GC is happening the CLR does trigger the finalizaion thread to start cleaning up. 当发生完整的GC时,CLR会触发终止线程以开始清理。 This does not help since you do allocate faster than you can finalize (writing to stdout is very slow) and your finalization queue will become bigger and bigger leading to slower and slower finalization times. 这没有用,因为你的分配速度比你最终确定的要快(写入stdout非常慢),你的终结队列会变得越来越大,导致终结时间越来越慢。

I have not measured it but I think even an empty finalizer will cause this issue since the increased object lifetime and two finalization queue handling (finalizer queue and f-reachable queue) do impose enough overhead to make finalization slower than allocation. 我还没有测量它,但我认为即使是一个空的终结器也会导致这个问题,因为增加的对象生命周期和两个终结队列处理(终结器队列和f可达队列)确实会产生足够的开销来使终结比分配慢。

You need to remember that finalization is an inherent asynchronous operation with no execution guarantees at a specific point of time. 您需要记住,finalization是一个固有的异步操作,在特定的时间点没有执行保证。 The CLR will never wait to clean all pending finalizers before allowing additional allocations. 在允许额外分配之前,CLR永远不会等待清除所有挂起的终结器。 If you allocate on 10 threads there will still be one finalizer thread cleaning up after you. 如果你分配10个线程,你仍然会有一个终结器线程清理。 If you want to rely on deterministic finalization you will need to wait by calling GC.WaitForPendingFinalizers() but this will bring your performance to a grinding halt. 如果你想依赖确定性的终结,你需要等待调用GC.WaitForPendingFinalizers(),但这会使你的表现停滞不前。

Your OOM is therefore expected. 因此,您的OOM是预期的。

您应该使用AddMemoryPressure函数,否则垃圾收集器会低估及时清理这些对象的需要。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM