简体   繁体   English

线程内存泄漏

[英]Thread memory leak

I am trying to track down a memory leak in a larger C# program which spawns multiple threads. 我试图在更大的C#程序中追踪内存泄漏,该程序产生多个线程。 In the process, I have created a small side program which I am using to test some basic things, and I found some behavior that I really do not understand. 在这个过程中,我创建了一个小方程序,我用它来测试一些基本的东西,我发现了一些我真的不明白的行为。

class Program
{
    static void test()
    {
    }

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

Running this program, I see that the memory usage of the program increases steadily without stopping. 运行这个程序,我看到程序的内存使用量稳步增加而没有停止。 In just a few minutes the memory usage goes well over 100MB and keeps climbing. 在短短几分钟内,内存使用量超过100MB并继续攀升。 If I comment out the line test_thread.Start();, the memory used by the program maxes out at about a few megabytes, and levels out. 如果我注释掉test_thread.Start();行,那么程序使用的内存最大可以达到几兆字节,并且会升级。 I also tried forcing garbage collection at the end of the while loop using GC.Collect(), but it did not seem to do anything. 我还尝试使用GC.Collect()在while循环结束时强制进行垃圾收集,但它似乎没有做任何事情。

I thought that the thread would be dereferenced as soon as the function is finished executing allowing the GC to mop it up, but this doesn't seem to be happening. 我认为一旦函数完成执行允许GC拖动它就会取消引用该线程,但这似乎并没有发生。 I must not be understanding something deeper here, and I would appreciate some help with fixing this leak. 我不能在这里深入理解更深层次的东西,我将非常感谢帮助解决这个问题。 Thanks in advance! 提前致谢!

This is by design, your test program is supposed to exhibit runaway memory usage. 这是设计使然,您的测试程序应该显示内存使用失控。 You can see the underlying reason from Taskmgr.exe. 您可以从Taskmgr.exe中查看基本原因。 Use View + Select Columns and tick "Handles". 使用View + Select Columns并勾选“Handles”。 Observe how the number of handles for your process is steadily increasing. 观察您的过程的句柄数量是如何稳步增加的。 Memory usage goes up along with that, reflecting the unmanaged memory used by the handle objects. 内存使用量随之增加,反映了句柄对象使用的非托管内存。

The design choice was a very courageous one, the CLR uses 5 operating system objects per thread. 设计选择非常勇敢,CLR每个线程使用5个操作系统对象。 Plumbing, used for synchronization. 管道,用于同步。 These objects are themselves disposable, the design choice was to not make the Thread class implement IDisposable. 这些对象本身是一次性的,设计选择是不要使Thread类实现IDisposable。 That would be quite a hardship on .NET programmers, very difficult to make the Dispose() call at the right time. 这对.NET程序员来说非常困难,很难在正确的时间调用Dispose()。 Courage that wasn't exhibited in the Task class design btw, causing lots of hand-wringing and the general advice not to bother . 在任务类设计中没有表现出来的勇气,导致大量的手工拧干和一般建议不要打扰

This is not normally a problem in a well-designed .NET program. 不是通常一个精心设计的.NET程序有问题。 Where the GC runs often enough to clean up those OS objects. GC运行的频率足以清理那些OS对象。 And Thread objects are creating sparingly, using the ThreadPool for very short running threads like your test program uses. 并且Thread对象正在谨慎创建,使用ThreadPool进行非常短的运行线程,就像测试程序使用的那样。

It can be, we can't see your real program. 它可以,我们看不到你的真实节目。 Do beware of drawing too many conclusions from such a synthetic test. 请注意从这样的综合测试中得出太多结论。 You can see GC statistics with Perfmon.exe, gives you an idea if it is running often enough. 您可以使用Perfmon.exe查看GC统计信息,让您了解它是否经常运行。 A decent .NET memory profiler is the weapon of choice. 一个体面的.NET内存分析器是首选的武器。 GC.Collect() is the backup weapon. GC.Collect()是备用武器。 For example: 例如:

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);
    }
}

And you'll see it bounce back and forth now, never getting much higher than 4 MB. 而且你会看到它现在来回反弹,永远不会超过4 MB。

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

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