【问题标题】:Undesirable Garbage Collection不受欢迎的垃圾收集
【发布时间】:2012-01-05 17:58:34
【问题描述】:

在 Andrew Troelsen 的 “C# 2010 和 .NET 4 平台”一书中的标题 “强制垃圾收集” 中写道:

“同样,.NET 垃圾收集器的全部目的是代表我们管理内存。但是,在极少数情况下,使用 GC.Collect() 以编程方式强制垃圾收集可能是有益的。特别是:

• 您的应用程序即将进入您不希望被可能的垃圾回收中断的代码块。 ... "

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

谢谢!

【问题讨论】:

  • 请特别注意“一些非常罕见的情况”,不要在你的代码中到处使用 GC.Collect()。

标签: c# .net garbage-collection


【解决方案1】:

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

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

【讨论】:

  • 如果你知道它没有问题,甚至比你可能想要的场景更罕见......
  • @TonyHopkinson:相对罕见,但并非闻所未闻。更准确地说,您可能很清楚现在的问题比不久的将来要少
  • 打破规则总是有理由的,并不是说你不应该这样做,只是在你打破它们之前,你应该了解它们,这样你才能有效地打破它们。
【解决方案2】:

我经营着一个与食谱相关的网站,我在内存中存储了大量的食谱图及其成分使用情况。由于我将这些信息转换为快速访问的方式,当应用程序加载时,我必须将数个 gig 的数据加载到内存中,然后才能将数据组织成一个非常优化的图表。我在堆上创建了大量的小对象,一旦构建了图形,它们就会变得无法访问。

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

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

【讨论】:

    【解决方案3】:

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

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

    在错误的位置调用Collect 会使您的表现更差。更糟糕的是,依赖它会使您的代码非常脆弱。调用Collect 有益或最终无害所需的罕见条件可以通过简单更改代码完全撤消,这将导致意外的OOM、性能缓慢等。

    【讨论】:

      【解决方案4】:

      我在性能测量之前调用它,这样 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 恕我直言。

      【讨论】:

        【解决方案5】:

        这种情况非常少见,但 GC 可能是一个成本适中的过程,因此如果存在对时间敏感的特定部分,您不希望该部分被 GC 中断。

        【讨论】:

          【解决方案6】:

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

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

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

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

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

          根据我自己的观察(非常非正式),第 0 代收集时间小于 40 毫秒,通常要少得多。完整的第 2 代可以运行约 100 毫秒,可能更多。

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-01-21
            • 1970-01-01
            • 2012-06-28
            • 1970-01-01
            • 1970-01-01
            • 2018-12-30
            • 1970-01-01
            • 2010-12-13
            相关资源
            最近更新 更多