为什么这个对象没有被垃圾回收?

Why is this object not garbage collected?

我对 C# 的垃圾收集器仍然有些困惑。文档指出,当没有更多引用指向某个对象时,该对象将被垃圾回收。

所以我的问题是,为什么这个对象没有立即被垃圾回收?没有提及它。假设背景 class 本身也没有创建任何引用。

public void StartGame() {
   // the background instance is created, but no reference is kept:
   new Background("landscape.png", speed);
}

C# 中的垃圾收集器仅在某些时刻被调用并且是分代的。这意味着在第一次 GC 中没有引用的对象将升级 1 代。最低代比其他代更频繁地被垃圾收集。

您可以阅读这篇文章以了解更多信息:https://msdn.microsoft.com/en-us/library/ee787088%28v=vs.110%29.aspx

您也可以调用 GC.Collect 方法,但不推荐这样做,因为 .NET 几乎能够处理自己的内存,调用此方法有点违背了整个目的。

您所描述的更类似于通过引用计数进行的内存管理,它会在访问引用计数器时检查是否释放内存,通常是在引用对象被访问时建造或摧毁。

垃圾收集是一个略有不同的概念。在 GC 支持的环境中直接释放对象通常是不允许或不推荐的。相反,垃圾收集器 运行 偶尔 (运行时间决定何时这样做),它会找到所有不再引用的对象并释放占用的内存通过这些。关键是,你不应该打扰(因为你对此无能为力)它什么时候会发生。

垃圾收集器就像在您附近行驶的垃圾车。一旦将垃圾放在路边,您就不会去捡垃圾。您必须等待它按照自己的条件出现。

垃圾收集器在理论上非常简单:停止世界,确定不再使用的内容,收集,恢复世界。但是因为这需要时间,开发人员使用复杂的算法来决定收集器何时启动以及收集什么,以使您的程序 运行 尽可能顺利。有些垃圾通常不会影响您的程序。

如果您希望您的对象在超出范围后立即被收集,并且您可能正在使用 finalizer 来测试它。不!取而代之的是,实现 IDisposable 并在完成后立即自行调用 Dispose 方法。你不能依赖垃圾收集器随时收集你的对象,如果有的话。这就是为什么 BCL I/O 类 都实现 IDisposable 来刷新流、关闭连接和清理内存。

或者,如果您想保留您的对象,您需要保留对它的(间接)引用。该对象可能会在下一个垃圾回收周期中被回收。


好吧,有一种不推荐强制垃圾车收集垃圾的方法,使用GC.Collect:

GC.Collect();

这将暂时停止您的程序以收集所有垃圾。但是,这可能仍然无法清除您的 Background 对象,因为它存在于堆栈或其他地方。您无法预测 运行time 会将您的对象放置在何处以及何时释放它,因此在测试是否使用 GC.Collect 收集对象之前,请务必至少退出创建该对象的方法。

好吧,如果您想检查一个对象是否会被垃圾回收,您可以执行以下操作,通过执行以下操作在您的代码中测试它是否符合回收条件。

稍微修改您的方法以进行测试,或者您可以引用在 StartGame 方法中设置的字段。

public void StartGame(out WeakReference reference)
{
    reference = new WeakReference(new Background("landscape.png", speed));
}

调用您的方法后,您可以执行以下操作以查看它是否符合收集条件。

WeakReference reference;
StartGame(out reference);
GC.Collect();
GC.WaitForPendingFinalizers();
if (reference.IsAlive)
{
    Console.WriteLine("Background is not eligible for collection");
}

这仅用于测试,否则您不应该调用垃圾收集器。