不受欢迎的垃圾收集

本文关键字:不受欢迎 | 更新日期: 2023-09-27 18:19:29

在Andrew Troelsen的《C#2010和.NET 4平台》一书中的标题"强制垃圾回收"中写道:

"同样,.NET垃圾收集器的全部目的是代表我们管理内存。然而,在一些非常罕见的情况下,使用GC.Collect()以编程方式强制垃圾收集可能是有益的。具体来说:

•您的应用程序即将进入一个代码块,您不希望该代码块被可能的垃圾收集打断。。。。"

但是停下来!当垃圾回收不可取时,是否存在这种情况?我从未见过/读过这样的东西(当然是因为我的开发经验很少)。如果你在练习时做过类似的事情,请分享。对我来说,这是非常有趣的一点。

谢谢!

不受欢迎的垃圾收集

是的,垃圾收集绝对是不可取的:当用户正在等待某件事发生时,他们必须等待更长的时间,因为在垃圾收集完成之前,代码无法继续。

这就是Troelsen的观点:如果你有一个特定的点,你知道GC没有问题,并且很可能能够收集大量垃圾,那么它可能是一个好主意,可以在不太合适的时候触发它。

我运行了一个与食谱相关的网站,并在内存中存储了大量食谱及其配料使用情况的图表。由于我将这些信息转换为快速访问的方式,在应用程序加载时,我必须将几GB的数据加载到内存中,然后才能将数据组织到一个非常优化的图中。我在堆上创建了大量的小对象,一旦构建了图,这些对象就会变得不可访问。

这一切都是在web应用程序加载时完成的,可能需要4-5秒。完成后,我调用GC.Collect();,因为我宁愿现在重新声明所有内存,也不愿在垃圾收集器忙于清理所有这些短期对象时,在传入HTTP请求期间潜在地阻塞所有线程。我还认为最好现在就清理,因为这个时候堆的碎片可能更少,因为我的应用程序到目前为止还没有真正做任何其他事情。延迟这样做可能会导致创建更多的对象,并且在GC自动运行时需要对堆进行更多压缩。

除此之外,在我12年的.NET编程生涯中,我从未遇到过要强制运行垃圾收集器的情况。

建议您不要在代码中显式调用Collect。你能找到有用的情况吗?

其他人已经详细介绍了一些,毫无疑问还有更多。不过,首先要理解的是不要这样做。这是最后的手段,研究其他选项,了解GC是如何工作的,看看你的代码是如何受到影响的,遵循你的设计的最佳实践。

在错误的时间点调用Collect会使您的性能变差。更糟糕的是,依赖它会使代码变得非常脆弱。调用Collect所需的罕见条件是有益的,或者最终是无害的,只需对代码进行简单的更改就可以完全消除,这将导致意外的OOM、缓慢的性能等等。

我在性能测量之前调用它,这样GC就不会伪造结果。

另一种情况是测试内存泄漏的单元测试:

object doesItLeak = /*...*/; //The object you want to have tested
WeakReference reference = new WeakRefrence(doesItLeak); 
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Assert.That(!reference.IsAlive);

除此之外,我还没有遇到这样一种情况,它实际上会有所帮助
特别是在生产代码中,GC.Collect永远不应该在IMHO中找到。

这将是非常罕见的,但GC可能是一个中等成本的过程,因此如果有一个特定的部分是时间敏感的,您不希望该部分被GC中断。

您的应用程序即将输入您没有输入的代码块希望被可能的垃圾收集中断。。。

一个非常可疑的论点(尽管如此,它还是被大量使用)。

Windows不是实时操作系统。您的代码(线程/进程)总是可以被操作系统调度程序预先占用。您不能保证访问CPU。

因此,它可以归结为:GC运行的时间与时隙(~20毫秒)相比如何?

关于这方面的硬数据很少,我搜索了几次。

根据我自己的观察(非常非正式),0代的收藏是<40毫秒,通常要少得多。一个完整的第二代可以运行约100毫秒,可能更长。

因此,被GC中断的"风险"与被换成另一个进程的风险是相同的数量级。你无法控制后者。