2013-03-20 40 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函数,否则垃圾收集器会低估需要及时清理这些对象。