为什么不应该直接调用垃圾回收器的原因

12

我目前正在为公司撰写一篇关于如何避免在代码中直接调用垃圾回收器(例如处理COM对象时)的论文。

我知道这是不良做法,应该只在非常罕见的情况下考虑,但我似乎找不到告诉我为什么应该避免这样做的方法。我也不想依赖于“垃圾回收器比你聪明”的原则(即使这是真的 :-))

那么,你能告诉我为什么你认为应该避免直接调用垃圾回收器的一些线索吗?(性能影响?)或者,如果你有关于这个特定主题的链接,它们会非常有帮助。

提前感谢!

编辑:目前为止您提供的所有答案都非常有帮助。由于我无法验证每一个人(我可以吗?),我该怎么办?创建社区Wiki?

4个回答

4
主要原因是在GC中执行比必要更多的完整收集的程序会比其需要的速度慢。
考虑到这是一个更容易获得更好性能的情况,对我来说似乎是个不费吹灰之力的选择!
注:当使用COM对象时,直接调用GC不太可能解决您的问题。如果COM对象存在,则它们具有非零引用计数,并且无论进行多少次额外的GC调用都无法解决该问题。

但是另一方面,有些COM对象即使在COM对象具有0个引用计数之后仍然不会消失。我曾经看到过Excel COM对象发生这种情况。 - Vinko Vrsalovic
Vinko,这正是我文档中提到的例子。但是可以在不显式调用G.C.的情况下删除所有引用。 - Shimrod
是的,Excel 的问题通常是您的应用程序创建了对对象的引用,但实际上没有保留该引用(例如通过执行类似 Sheets(0).SomeSheetMethod 的操作,该操作创建了一个未保留并且不能显式释放的 Sheet 实例)。 - Hans Olsson
这正是我在我的文档ho1中所解释的内容 :-) 因此,一旦你理解了这一点,就没有必要调用G.C.。你知道这种行为是所有COM对象都普遍存在的,还是只有由Excel创建的对象存在这种情况? - Shimrod

2
通常的性能论点如下:
分代垃圾回收是快速的,因为它们依赖启发式算法,即许多分配的对象是短暂的(只要对象可达,它就是“活”的;垃圾回收的目的是检测“死”对象并回收它们的内存)。这意味着对象可以累积在一个特殊区域(“年轻代”)中;当该区域已满时,GC运行并清除活动对象,将它们(“物理地”)移入老年代。在大多数分代GC中,此操作意味着暂停(“停止世界”),这是可以容忍的,因为它很短(年轻代的大小有限)。世界在年轻代的收集期间暂停,允许有效处理年轻对象(即,在年轻对象字段中读取或写入引用仅是一次内存访问,无需考虑来自GC线程或增量标记&扫描的并发访问)。
像我上面描述的那样进行收集的年轻代是高效的,因为当年轻代被收集时,其中的大多数对象已经死亡,因此它们不会产生额外的成本。年轻代的最佳大小是最坏情况(所有年轻对象都是活的,这意味着最大暂停时间)和平均效率(当年轻代更大时,更多的对象有时间在收集之前死亡,从而降低了GC的平均成本)之间的权衡。
手动运行GC类似于使年轻代变短。这意味着更多的年轻对象将被提升到老年代,从而增加了年轻代的收集成本(必须清除更多对象)老年代的收集成本(要处理更多的旧对象)。

1

由于所有线程都需要停止才能进行收集,因此它会对性能产生影响。之后,它需要确定什么是被使用的,什么是不被使用的等等...

所有这些都需要时间,垃圾收集器只有在确定好处大于坏处时才会工作。

当您自己调用GC时,您很可能会过于频繁地调用它,这将增加在GC中花费的时间并减少在程序中花费的时间。


1
如果您需要调用 GarbageCollector 确保 COM 对象被释放,那么很可能是因为开发人员没有在应该使用 Dispose 和/或 using 时调用它们。因此,一个论点是这只是隐藏了糟糕的代码,最好修复糟糕的代码。

COM对象通常不支持“IDisposable”,因此这不会直接成为解决方案。 - Daniel Earwicker
@Daniel Earwicker:你误解了,我的意思是COM对象是一种非托管资源,你可能需要以某种方式清理它们(也许使用ReleaseComObject)在你的Dispose方法中。如果使用COM对象的类的开发人员已经编写了清理代码到Dispose中,那么调用该类的调用方应该调用Dispose或使用using - Hans Olsson
不,我懂的。这就是为什么我说“直接”的原因。 :) - Daniel Earwicker
@Daniel:那我误解了,所以我至少部分正确,我们中有一个人误解了。 - Hans Olsson

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