GC.Collect()

21

好的,我读了一些相关主题,现在让我们来假设一个情况。假设我有一个应用程序,在这个应用程序中,我会经常点击一个按钮,然后会发生很多事情,持续几分钟,然后它可能会闲置1小时,也可能只是1分钟。那么在这整个过程之后调用GC.Collect会是一个不错的选择吗?我的意思是,我知道此时我将不会使用我的应用程序,并且GC无法猜测。

7个回答

31

我看到有几个人过于推荐不要调用GC.Collect。

GC.Collect是有原因的,以下是我的建议,包括何时以及为什么要调用GC.Collect。

  1. 通常情况下,不必担心调用它,GC会自我调优并做出正确的事情。

  2. 有时候你会遇到确定需要调用它的情况,你所描述的情况正是调用它的恰当时机,实际上,Asp.Net在类似于你所描述的情况下会调用GC.Collect。

  3. GC自己很聪明,如果你调用了GC.Collect,GC可以覆盖你的决策并且不会进行垃圾回收(当你调用GC.Collect时,你必须设置一个标志来选择此行为),这是调用GC.Collect的推荐方式,因为它仍然让GC决定是否是好时机进行回收。

  4. 不要认为我的建议适用于调用GC.Collect的一般情况,除非你确实确定需要调用它,否则应该始终避免调用它,像你所描述的那种情况正是GC.Collect存在的原因。

  5. 调用它的好处是快速释放垃圾,通常你会关心以下情况:

    1. 您的内存不足,想要急切地回收,如果您的内存不足,GC也会变得积极,并且在机器上出现内存压力高的情况下会自动启动。
    2. 如果您想避免内存不足并且希望急切地进行回收。

希望能对您有所帮助。
谢谢


正如我所提到的,不要把我的建议视为普遍规则,这只是个例外情况;恰好符合你的情况 :) - mfawzymkh

20
几乎总是在原型设计或构建应用程序并测试其性能之前担心调用GC.Collect是一种过早的优化。垃圾回收器通常非常擅长在适当的时间收集内存。特别是如果系统中存在内存压力,它肯定会在应用程序空闲时运行收集。
更重要的是,遵循良好的分代GC分配实践(小对象,短时间使用等),您可能会获得所需的性能特征。如果经过性能剖析和良好的设计后仍然无法满足性能需求,您可以考虑使用GC.Collect作为解决方案。

好的,这似乎比我到处看到的“没有垃圾回收”更合理。 - devoured elysium
+1. 对于为什么几乎总是不需要使用 GC.Collect() 的很好的解释。 - Randolpho

7

几乎没有什么好的理由去调用 GC.Collect()

在你的情况下,绝对没有必要调用它。如果你闲置了一个小时,垃圾回收器进行自己的垃圾回收。


是的,但可能会在下一分钟内完成。但是当我结束处理时,我知道如果花费几秒钟来执行GC.Collect(),那么此时不会有性能问题。 - devoured elysium
4
调用GC.Collect()实际上可以延长对象的生命周期。 - Mitch Wheat
“提升对象的生命周期”是什么意思? - devoured elysium
8
当您调用GC.Collect()时,任何未被回收的对象都会被提升,以便它们可以在其他地方存活更长时间。请搜索“分代垃圾回收”以获取更多信息。尽管您的应用程序现在可能没有性能问题,但它可能会在以后受到影响。 - Matthew Steeples
啊,是的,我也读过这个。我没有考虑到这一点,这确实是一个好观点。 - devoured elysium
嗯,现在我想起来了,这不会是一个很大的问题,因为非强制GC也会发生这种情况。可能会发生的情况是,长期来看,我会在第一代和第二代中拥有更多的对象,但我可以确保我的第零代空间始终为空。对我来说,这似乎并不是一个本质上的坏事。 - devoured elysium

4

我希望您知道调用GC.Collect不会导致更多(或更少)的对象被收集。

如果您想要优化时间,您是否知道GC在应用程序中收集对象所需的时间?在桌面操作系统(XP、Vista等)上,CLR使用并发GC,它可以在不暂停应用程序中的所有线程的情况下运行整个收集过程。

不建议显式调用GC.Collect,因为:

  1. 它会扰乱CLR GC调整算法。调整器会自动确定何时触发GC,强制手动GC会干扰其计算。

  2. 通过手动强制进行收集,您可能会使对象向上提升到较高的一代 - 这些对象如果在GC决定启动之前“孤立”可能已经被收集了。

您可能会发现有趣的是,在.NET 4.0中引入了GC通知机制,用于这些场景。


谢谢回复。我不确定我理解了以下观点:“您知道GC在应用程序中收集对象所需的时间吗?在桌面操作系统(XP,Vista等)上,CLR使用并发GC,并且可以在不暂停应用程序中的所有线程的情况下运行以进行收集。”根据我所读的,似乎GC收集非常快(对于第0代来说,不应该需要超过页面错误的时间)。 至于您的第二个观点,那似乎不是很好的论据。同样适用于常规GC。 - devoured elysium
如果GC收集相当快,为什么要手动调用以节省时间呢? 至于第二点,当发生常规GC时也会发生同样的事情,但是然后GC决定何时运行,这又取决于GC调整算法。它可能会发现您的应用程序分配对象的方式存在模式,并利用这些知识在内存方面在最佳时间触发GC - 在这种情况下稍晚一些。这将收集更多的对象并促进更少的对象。 - Senthil Kumar
它只给我留下了这样的印象,人们认为垃圾收集器现在比我们程序员更懂我们程序的工作原理。虽然我知道我的进程每当电脑闲置2分钟时就会发生,但我很难相信垃圾收集器也会知道同样的事情。关于何时进行最佳时间GC和手动GC的整个争论似乎并不牢固,仅此而已。 - devoured elysium
如果您在堆中创建每个单独的对象,并且可以计算代码中所有可能的控制排列,那么您比垃圾收集器更了解情况。即使如此,GC也可能考虑到可用的物理内存 - 例如,如果存在大量分页,则可能决定将其推迟。这些是您无法控制的事情。代码是静态的,除非在非常特殊的情况下,否则我认为很难预测在GC.Collect时运行时条件的优势。 - Senthil Kumar

1

要知道何时调用GC.Collect(),您需要了解与运行时相关的特定收集器的详细信息以及可以通过收集解决的弱点的详细信息。

换句话说,如果您真的知道何时需要调用GC.Collect(),并且这不是您在其他代码中做得不好的事情,那么您可能正在为CLR Internals工作,并且可以修复问题。


我的观点主要是:如果我们GC.Collect(),不会发生任何错误,对吧?如果没有问题,那么现在我们知道至少有一段时间我们的进程将处于空闲状态,而不是在某个随机时刻完成它,那么我们就没有什么可失去的了。或者我漏掉了什么吗?谢谢。 - devoured elysium
是的,调用GC.Collect()可能会对程序的性能产生毁灭性的影响。或者它可能什么也不做。重要的是要记住:与Java早期相比,现代GC非常擅长它们的工作,并且几乎从不是应用程序性能问题的罪魁祸首。事实上,.NET和Java GC比许多C/C++分配器实现更快。 - Sam Harwell
它如何对我的程序性能产生毁灭性影响?我认为,如果它恰好发生在我的进程运行时而不是我知道它将处于空闲状态时,它将对我的程序性能产生毁灭性影响。 - devoured elysium
这完全取决于GC.Collect()及其底层技术的实现。例如,托管系统可能使用跨进程公共分配器,其中GC.Collect()调用会冻结所有应用程序而不仅仅是一个。也许某个地方有一个可以调用GC.Collect()的地方,但如果您知道那个地方,那么您可能不需要向我们询问任何问题。 :) - Sam Harwell

0

通常只有在尝试分配新内存时才会调用 GC。如果您没有耗尽内存,调用 GC 将不会带来任何性能提升。您必须拥有一个非常资源密集型的应用程序,甚至才能接近今天计算机的 RAM 限制。

如果您的程序有大量外部资源(如文件或 COM/DCOM 引用),则可能需要调用 GC。

如果调用 GC 能让您心安,则请继续。这可能不会有所帮助,但肯定不会有害。


嗯,人们确实会在旧机器上运行软件。当我帮助人们清理他们的机器以使其运行更好时,我经常发现有些软件使用50-100 MB的工作集,只是为了等待互联网电话或其他一些荒谬的事情。在1 GB的机器上,这些东西很快就会累积起来! - Zan Lynx

0
正如其他帖子中提到的,垃圾回收器比你更了解何时开始垃圾回收。您的应用程序中没有按钮被点击并不意味着现在就是开始清理的时候,因为GC在移动对象时会执行某些锁定操作,如果滥用GC.Collect可能会导致性能下降。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接