线程内存泄漏

Ser*_*giy 8 c# multithreading memory-leaks

我试图在更大的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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

运行这个程序,我看到程序的内存使用量稳步增加而没有停止.在短短几分钟内,内存使用量超过100MB并继续攀升.如果我注释掉test_thread.Start();行,那么程序使用的内存最大可以达到几兆字节,并且会升级.我还尝试使用GC.Collect()在while循环结束时强制进行垃圾收集,但它似乎没有做任何事情.

我认为一旦函数完成执行允许GC拖动它就会取消引用该线程,但这似乎并没有发生.我不能在这里深入理解更深层次的东西,我将非常感谢帮助解决这个问题.提前致谢!

Han*_*ant 11

这是设计使然,您的测试程序应该显示内存使用失控.您可以从Taskmgr.exe中查看基本原因.使用View + Select Columns并勾选"Handles".观察您的过程的句柄数量是如何稳步增加的.内存使用量随之增加,反映了句柄对象使用的非托管内存.

设计选择非常勇敢,CLR每个线程使用5个操作系统对象.管道,用于同步.这些对象本身是一次性的,设计选择是不要使Thread类实现IDisposable.这对.NET程序员来说非常困难,很难在正确的时间调用Dispose().在任务类设计中没有表现出来的勇气,导致大量的手工拧干和一般建议不要打扰.

不是通常一个精心设计的.NET程序有问题.GC运行的频率足以清理那些OS对象.并且Thread对象正在谨慎创建,使用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);
    }
}
Run Code Online (Sandbox Code Playgroud)

而且你会看到它现在来回反弹,永远不会超过4 MB.