使用GC.Collect()有什么问题?

时间:2020-03-06 14:34:34  来源:igfitidea点击:

尽管我确实理解使用此功能的严重含义(或者至少是我的想法),但我看不到为什么它成为受人尊敬的程序员永远不会使用的东西之一,即使那些甚至不知道是为了什么

假设我正在开发一个应用程序,该应用程序的内存使用量会根据用户的操作而变化很大。应用程序生命周期可以分为两个主要阶段:编辑和实时处理。在编辑阶段,假设创建了数十亿甚至数万亿个对象。其中有些很小,有些则没有,有些可能没有终结器,有些可能没有终结器,并且假设它们的生命周期从几毫秒到很长的小时不等。接下来,用户决定切换到实时阶段。在这一点上,假设性能起着根本性的作用,并且程序流程中的任何细微改动都可能带来灾难性的后果。然后,通过使用对象池等将对象创建减少到最小程度,但是GC意外插入并扔掉所有对象,然后有人死亡。

问题:在这种情况下,在进入第二阶段之前调用GC.Collect()是否明智?

毕竟,这两个阶段永远不会在时间上相互重叠,并且GC可以收集的所有优化和统计信息在这里都没有多大用处...

注意:正如某些人指出的那样,.NET可能不是适用于此类应用程序的最佳平台,但这超出了此问题的范围。目的是阐明GC.Collect()调用是否可以改善应用程序的整体行为/性能。我们都同意,我们在这种情况下会做这种事情的情况极为罕见,但是GC再次尝试猜测并在大多数情况下都做得很好,但这仍然只是猜测。

谢谢。

解决方案

我认为我们对这种情况是正确的,但是我不确定该API。

Microsoft说,在这种情况下,应增加内存压力,以提示GC尽快执行收集。

好吧,显然,我们不应该使用具有非实时垃圾收集功能的语言编写具有实时需求的代码。

在阶段定义明确的情况下,触发垃圾收集器没有问题。但是这种情况非常罕见。问题在于,许多开发人员将尝试使用这种方法以杂技主义的方式掩盖纸质问题,并且不加选择地添加它会导致性能问题。

在某些情况下它很有用,但通常应避免使用它。我们可以将其与GOTO进行比较,也可以将其与轻便摩托车相提并论:在需要时可以这样做,但我们不会告诉朋友。

根据我的经验,从来不建议在生产代码中调用GC.Collect()。是的,在调试中,它具有帮助澄清潜在的内存泄漏的优点。
我想我的根本原因是程序员已经比我聪明得多地编写了GC并对其进行了优化,如果我觉得需要调用GC.Collect()的话,这是我走开了一条线索某处。
在情况下,听起来好像并没有实际的内存问题,只是我们担心集合将给过程带来什么不稳定性。看到它不会清除仍在使用的对象,并且可以非常迅速地适应不断增长的需求和降低的需求,我想我们将不必担心。

最重要的是,我们可以分析应用程序并查看这些其他集合如何影响事物。我建议我们不要使用它,除非我们要进行简介。 GC旨在照顾好自己,随着运行时的发展,它们可能会提高效率。我们不希望到处乱七八糟的代码可能会破坏工作,并且无法利用这些改进。对于使用foreach而不是for有一个类似的论点,那就是,可以在foreach的基础上增加将来的改进,并且不必更改代码就可以利用。

如果在生产代码中调用GC.Collect(),则实际上是在声明我们知道更多信息,然后才知道GC的作者。可能是这种情况。但是通常不是,因此强烈建议不要这样做。

调用GC.Collect()会强制CLR进行堆栈遍历,以查看是否可以通过检查引用来真正释放每个对象。如果对象数量很多,这将影响可伸缩性,并且众所周知,它经常触发垃圾回收。信任CLR,并在适当时让垃圾收集器自行运行。

调用GC.Collect()的最大原因之一是,我们刚刚执行了一个重大事件,该事件会产生大量垃圾,例如我们所描述的。在这里调用GC.Collect()可能是个好主意;否则,GC可能无法理解这是一次"一次性"事件。

当然,我们应该对其进行概要分析,然后自己看看。

来自Rico的博客...

Rule #1
  
  Don't.
  
  This is really the most important
  rule.  It's fair to say that most
  usages of GC.Collect() are a bad idea
  and I went into that in some detail in
  the orginal posting so I won't repeat
  all that here.  So let's move on to...
  
  Rule #2
  
  Consider calling GC.Collect() if some
  non-recurring event has just happened
  and this event is highly likely to
  have caused a lot of old objects to
  die.
  
  A classic example of this is if you're
  writing a client application and you
  display a very large and complicated
  form that has a lot of data associated
  with it.  Your user has just
  interacted with this form potentially
  creating some large objects... things
  like XML documents, or a large DataSet
  or two.  When the form closes these
  objects are dead and so GC.Collect()
  will reclaim the memory associated
  with them...

因此,听起来这种情况可能属于规则2,我们知道在一段时间内许多旧物体已经死亡,并且这种情况不会重复发生。但是,不要忘了Rico的离别词。

Rule #1 should trump Rule #2 without
  strong evidence.

测量,测量,测量。

好吧,GC是我之间有爱/恨关系的事情之一。我们过去通过VistaDB破坏了它,并在博客上对此进行了博客。他们已经修复了它,但是花很长时间才能从他们那里得到关于此类问题的修复程序。

GC非常复杂,很难一一对应所有方法。 MS的工作做得相当不错,但有时可能会欺骗GC。

通常,我们不应该添加Collect,除非我们知道事实是我们刚刚转储了很多内存,并且如果GC现在不清理它,它将陷入中期危机。

我们可能会用一系列错误的GC.Collect语句破坏整个机器。对collect语句的需求几乎总是指向更大的基础错误。内存泄漏通常与引用有关,并且对它们的工作原理缺乏了解。或者在不需要的对象上使用" IDisposable",并给GC带来更高的负载。

通过系统性能计数器密切关注在GC中花费的时间百分比。如果我们在GC中看到应用使用了20%或者更多的时间,则表示我们存在严重的对象管理问题(或者异常的使用模式)。我们希望始终尽量减少GC花费的时间,因为它可以加快整个应用程序的速度。

同样重要的是要注意,服务器上的GC与工作站上的GC不同。我已经看到了一些难以发现问题的小难题,因为人们没有测试他们两个(甚至都不知道他们是两个)。

为了尽可能全面地回答我,如果我们也针对该平台,也应该在Mono下进行测试。由于它是完全不同的实现,因此可能会遇到与MS实现完全不同的问题。

.NET Framework本身从未设计为可以在实时环境中运行。如果我们确实需要实时处理,则可以使用不基于.NET的嵌入式实时语言,也可以使用Windows CE设备上运行的.NET Compact Framework。

它出什么问题了?我们正在对垃圾回收器和内存分配器进行第二次猜测,在它们之间,我们对应用程序在运行时的实际内存使用情况的了解要比我们大得多。

调用GC.Collect()的愿望通常是试图掩盖我们在其他地方犯的错误!

如果我们发现忘记处置不再需要的东西,那就更好了。