C#BackgroundWorker在重新启动时吃掉内存

时间:2018-07-20 23:46:54

标签: c# multithreading winforms backgroundworker

我用以下代码创建了一个空窗体,以测试连续重新启动后台工作程序的性能;

        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()的情况下处置后台线程?

感谢您的时间。

1 个答案:

答案 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。