我用以下代码创建了一个空窗体,以测试连续重新启动后台工作程序的性能;
public Form1()
{
InitializeComponent();
BackgroundWorker bw = new BackgroundWorker();
// Do work on Background thread
bw.DoWork += DoWork_WorkerThread;
bw.RunWorkerAsync();
// Return to UI thread and Restart the worker
bw.RunWorkerCompleted += DoWork_WorkerThreadCompleted;
}
private void DoWork_WorkerThread(object sender, DoWorkEventArgs e)
{
System.Threading.Thread.Sleep(100);
//GC.Collect();
}
private void DoWork_WorkerThreadCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Restart the worker.
// We can verify its the background worker because commenting this out relieves the issue.
((BackgroundWorker)sender).RunWorkerAsync();
}
您可以看到,每次从DoWork_WorkerThreadCompleted
重新启动工作程序时,任务管理器中的内存就会跳跃。
我注意到,如果我将GC.Collect()
扔进工作线程中,问题就消失了。我是不正确地重新启动工作线程,还是有办法确保在不调用GC.Collect()
的情况下处置后台线程?
感谢您的时间。
答案 0 :(得分:3)
在少数情况下,需要显式调用GC。仅仅因为重复执行操作时内存增加,并不一定意味着内存泄漏。 .NET for仅在真正需要时才释放内存,例如内存压力很高时。调用GC.Collect
只会导致问题。
评论工人的开始并不能真正证明任何事情。尽管是的,在这种情况下,您已阻止了内存的增加,但是,您还消除了在工作程序运行到完成时释放内存的可能性,并允许有足够的时间或事件再次释放内存。 您永远无法真正高度肯定地预测何时将发生GC。不能忍受的呼叫GC.Collect
。
您可以用另一种方式思考问题。想象一下,分配一个您知道的对象使用合理数量的RAM。
void DoSomething()
{
var ob = new MyObjectRequiringReasonableAmountOfRam();
// 'ob' goes out of scope and is now a candidate for GC at some time or other
}
现在调用此方法将分配对象并超出范围,是的,内存可能会增加,但是认为存在内存泄漏为时尚早。当然,注释掉该代码可以消除“泄漏”的症状,但同时也可以删除注释掉您的工作线程的功能。
现在,如果除了调用上述方法之外,我还要调用:
void DoSomethingSlightlyMoreDrastic()
{
var ob = new MyObjectRequiringALargerAmountOfRam();
// 'ob' goes out of scope and is now a candidate for GC at some time or other
}
.... NET看到此分配需要更多的内存,并考虑了系统上的所有其他因素,例如您的进程分配了多少内存;来自所有其他进程的内存;计算机中的RAM以及GC如何工作的规则,此时您的第一个对象完全有可能是GC。