线程内存泄漏
本文关键字:泄漏 内存 线程 | 更新日期: 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。