4

我编写了一个简单的 .net 表单应用程序来测试 .NET 的一些行为,了解它如何与垃圾收集器一起处理内存以进行清理。

表单应用程序 GUI 如下所示:

应用程序图形用户界面

后面的代码是这样的:

public partial class Form1 : Form
{
    private readonly IList<byte[]> _btyList = new List<byte[]>();

    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        int i = 0;

        while (i < 3)
        {
            byte[] buffer = File.ReadAllBytes(@"C:\PFiles\20131018_nl_metro holland.pdf");
            _btyList.Add(buffer);
            i++;
        }
    }

    private void button2_Click(object sender, EventArgs e)
    {
        int i = 0;

        while (i < _btyList.Count)
        {
            _btyList[i] = null;
            i++;
        }
    }

    private void button3_Click(object sender, EventArgs e)
    {
        GC.Collect();
    }
}

当我将几个字节数组添加到字节数组的私有列表时,它(当然)会影响应用程序的内存使用:

添加字节数组后的内存使用情况

现在,当我按下清除内存按钮时,内存使用量将保持不变。我可以等待几个小时,但它不会改变。如果我按下垃圾收集按钮(在清除内存之后),它将立即释放内存:

垃圾收集后的内存使用情况

问题是:为什么垃圾收集器在这种情况下不起作用?

4

4 回答 4

4

垃圾收集器没有运行,因为它不需要。如果内存不低,则无需收集。

如果您有 4GB 内存,则 360MB 可能低于收集阈值。

一般来说,您不应该担心或考虑 GC 何时运行,除非您正在编写时间或内存关键代码。

参考:垃圾收集的基础知识

了解 .NET 中的垃圾收集

当下列条件之一为真时,就会发生垃圾收集:

  • 系统物理内存不足。

  • 托管堆上分配的对象使用的内存超过了可接受的阈值。随着流程的运行,此阈值会不断调整。

  • GC.Collect 方法被调用。在几乎所有情况下,您都不必调用此方法,因为垃圾收集器会持续运行。此方法主要用于特殊情况和测试。

于 2013-10-19T09:22:28.717 回答
3

垃圾收集相对较慢,CLR 不会在每次内存空闲时都这样做,只有在需要时才这样做。所以“释放”你的数组不会触发它。

您的实验取得了圆满成功:您已经了解了垃圾收集的工作原理。

于 2013-10-19T09:22:48.820 回答
2

GC 在触发发生时运行。触发器可能是分配失败或 Windows 生成的内存不足事件。

将变量设置为 null 不会以任何方式跟踪,也不是触发器。

您的案例是一个有效的案例, GC.Collect因为您了解 GC 所没有的释放模式。

于 2013-10-19T09:24:02.940 回答
2

它的行为正确。Garbage collector run is indeterministic,您无法确定它将在什么时间运行。

一旦感觉需要新的内存分配需要释放一些先前分配的内存,GC 就会收集内存。

来自MSDN -

每次创建新对象时,公共语言运行库都会从托管堆中为该对象分配内存。只要托管堆中有可用的地址空间,运行时就会继续为新对象分配空间。然而,记忆不是无限的。最终,垃圾收集器必须执行收集以释放一些内存。垃圾收集器的优化引擎根据正在进行的分配确定执行收集的最佳时间。当垃圾收集器执行收集时,它会检查托管堆中不再被应用程序使用的对象,并执行必要的操作来回收它们的内存。

于 2013-10-19T09:20:06.160 回答