2014-09-05 63 views
6

我想追查一个更大的C#程序中产生多个线程的内存泄漏。在这个过程中,我创建了一个我用来测试一些基本事物的小程序,并且发现了一些我实际上不理解的行为。线程内存泄漏

class Program 
{ 
    static void test() 
    { 
    } 

    static void Main(string[] args) 
    { 
     while (true) 
     {    
      Thread test_thread = new Thread(() => test()); 
      test_thread.Start(); 
      Thread.Sleep(20); 
     } 
    } 
} 

运行这个程序,我发现程序的内存使用量稳步增加而不停止。在短短几分钟内,内存使用量超过了100MB,并持续攀升。如果我注释掉test_thread.Start()这一行,程序使用的内存最大值大约为几兆字节,并保持稳定。我也尝试使用GC.Collect()在while循环结束时强制垃圾收集,但它似乎没有做任何事情。

我认为一旦函数完成执行后允许GC拖动它,线程就会被解除引用,但这似乎并没有发生。我不应该在这里更深入地理解某些东西,我希望能够帮助我们解决这个问题。提前致谢!

回答

9

这是设计,你的测试程序应该表现出失控的内存使用。您可以从Taskmgr.exe中查看潜在的原因。使用视图+选择列并勾选“处理”。观察您的流程手柄数量如何稳步增加。内存使用量随之增加,反映了句柄对象使用的非托管内存。

设计选择非常有勇气,CLR每个线程使用5个操作系统对象。管道,用于同步。这些对象本身是一次性的,设计选择是而不是使Thread类实现IDisposable。这对于.NET程序员来说是相当困难的,很难在正确的时间进行Dispose()调用。在任务类设计中没有展示的勇气,造成了大量的手动和general advice not to bother

这是不是通常是一个设计良好的.NET程序中的问题。 GC运行的频率足以清理这些操作系统对象。线程对象正在谨慎地创建,使用ThreadPool作为测试程序使用的非常短的运行线程。

它可以,我们无法看到您的真实程序。谨防从这样的综合测试中得出太多结论。您可以使用Perfmon.exe查看GC统计信息,为您提供一个足够频繁运行的想法。一个体面的.NET内存分析器是选择的武器。 GC.Collect()是备用武器。例如:

static void Main(string[] args) { 
    int cnt = 0; 
    while (true) { 
     Thread test_thread = new Thread(() => test()); 
     test_thread.Start(); 
     if (++cnt % 256 == 0) GC.Collect(); 
     Thread.Sleep(20); 
    } 
} 

而且你会看到它现在反弹,从未远远高于4 MB。