线程内存泄漏

本文关键字:泄漏 内存 线程 | 更新日期: 2023-09-27 18:20:47

我正试图在一个产生多个线程的较大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();这一行;,该程序使用的内存最大可达几兆字节,然后趋于平稳。我还尝试在while循环结束时使用GC.Collect()强制进行垃圾收集,但似乎没有任何作用。

我以为一旦函数执行完毕,线程就会被取消引用,从而允许GC将其清除,但这似乎并没有发生。我一定没有更深层次的理解,我希望能得到一些帮助来修复这个漏洞。提前感谢!

线程内存泄漏

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

设计选择非常大胆,CLR每个线程使用5个操作系统对象。卫浴,用于同步。这些对象本身是可丢弃的,设计选择是而不是使Thread类实现IDisposable。这对.NET程序员来说是相当困难的,很难在正确的时间调用Dispose()。任务类设计中没有表现出的勇气,引起了很多人的绝望,一般建议不要打扰。

这不是设计良好的.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);
    }
}

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