c# - 为什么即使在强制垃圾回收时,已完成的线程使用的内存也不会被释放?

标签 c# .net multithreading memory garbage-collection

我有一个 C# WinForms 应用程序,其中按下一个按钮实例化一个对象,订阅它的事件,然后基于该对象的方法启动一个线程。该对象的方法使用大量内存,但一旦线程完成,我认为应该在调用 GC.Collect() 时释放它。然而,情况似乎并非如此。该程序最多可以使用 GB 的内存,所以这不是一个小问题,它似乎只有在关闭程序或再次按下按钮时才会被释放。

这是一个演示问题的示例应用程序:

using System;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private Worker worker;

        private void button1_Click(object sender, EventArgs e)
        {
            worker = new Worker();
            worker.Finished += worker_Finished;

            Thread thread = new Thread(new ThreadStart(worker.DoWork));
            thread.IsBackground = true;
            thread.Start();
        }

        void worker_Finished(object sender, EventArgs e)
        {
            worker.Finished -= worker_Finished;
            GC.Collect();
            MessageBox.Show((Environment.WorkingSet / 1024 / 1024).ToString() + " MB in use");
        }
    }

    public class Worker
    {
        public event EventHandler Finished;
        protected virtual void OnFinished(EventArgs e)
        {
            EventHandler handler = Finished;
            if(handler != null)
            {
                handler(this, e);
            }
        }

        public void DoWork()
        {
            Random random = new Random();
            string[] list = new string[10000000];
            for(int i = 0; i < list.Length; i++)
            {
                list[i] = random.NextDouble().ToString();
            }
            //list = null;
            OnFinished(new EventArgs());
        }
    }
}

请注意,在这种情况下,取消注释行 list = null; 似乎可以解决问题,但我不确定为什么。在我的实际应用程序中,我曾尝试在函数结束前将所有大对象设置为 null,但似乎无济于事。所以这不是问题的完美重现,但希望有人可以解释这里发生的事情,这将帮助我解决实际问题。

此外,我知道 this question非常相似,但在我的例子中,我明确地强制进行垃圾回收。

最佳答案

垃圾收集是一件复杂的事情。这不仅仅是回收所有使用的内存的情况。对象之间存在复杂的关系需要考虑,因此 GC 必须确保它不会清理仍然可以在某处访问的对象。

因为您在 DoWork 方法完成之前引发事件,“list”变量仍在范围内,所以当您调用 Collect 时无法清除其内容。通过将变量设置为 null,您删除了对数组的引用,因此删除了它引用的所有 String 对象,因此可以通过调用 Collect 回收它们的所有内存。

关于c# - 为什么即使在强制垃圾回收时,已完成的线程使用的内存也不会被释放?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21470936/

相关文章:

c# - 如何知道哪个页面调用了Usercontrol

C#.NET : How to create self data relationship on single DataTable?

c# - 如何使我的表单标题栏遵循 Windows 深色主题?

.net - WPF UI 更新细节

c - 线程完成后如何继续运行进程?

multithreading - 当我在 Clojure 中定义来自多个线程的变量时会发生什么?

c# - 带有故障消息的 WCF 服务单向方法

c# - 需要有关我的界面代码的解释

c# - 优化两个非常大的列表的 C# 循环比较

c# - 通过数组循环项目的最佳实践