读了这个古老但经典的编写高性能托管应用程序-初级文档,我看到了下面的陈述
GC是自调优的,并将根据应用程序内存要求进行调整。在大多数情况下,以编程方式调用GC会阻碍这种调优。通过调用GC.Collect“帮助”GC很可能不会提高应用程序的性能。
我正在处理的应用程序,在给定的时间点,消耗大量的内存。当我在消耗内存的代码中完成时,我将调用GC.Collect。如果我不这样做,我就会走出记忆异常。这种行为是无可辩驳的,但粗野地说,有30%的时候,我都会失去记忆。在添加GC.Collect之后,我从来没有从内存异常中得到这个结果。即使这份最佳实践文件提出反对意见,我的行动是否正当?
发布于 2011-03-09 08:51:29
GC中的部分内容是内存中的对象是分代的,因此早期的收集比其他的更频繁。这有助于节省性能,因为不总是试图收集长期存在的对象。
考虑到这一点,当您自己打电话给GC.Collect()
时,可能会发生两件事。首先,您最终会花费更多的时间进行收集。这是因为除了手动的GC.Collect()之外,正常的背景集合仍然会发生。第二种情况是,您将把内存保存得更长,因为您将一些东西强加到不需要使用的高阶生成中。换句话说,自己使用GC.Collect()几乎总是一个坏主意。
在一些情况下,垃圾收集器的性能并不总是很好。其中之一是大型对象堆。对于大于一定大小的对象(80,000字节,IIRC,但这可能是旧的),这是一个特殊的生成。这一代人几乎从来没有被收集,几乎从来没有压实过。这意味着,随着时间的推移,您可能会在内存中出现许多无法释放的大漏洞。物理内存并没有被实际使用,也可以用于其他进程,但它仍然占用进程中的地址空间,默认情况下,您的地址空间限制在2GB以内。
这是一个非常常见的OutOfMemory异常源--您实际上并没有使用那么多内存,但是大对象堆中的漏洞占用了所有这些地址空间。到目前为止,这种情况最常见的方式是反复附加到大型字符串或文档中。这可能不是您的问题,因为在这种情况下,对GC.Collect()的任何调用都不会加重LOH,但在您的示例中,它似乎确实有所帮助。但是,这是我看到的绝大多数OutOfMemory异常的来源。
垃圾收集器并不总是运行良好的另一个地方是,当某些事情导致对象保持根时。一个例子是事件处理程序可以防止对象被收集。解决这一问题的方法是确保订阅事件的每个+=
操作都有一个相应的-=
操作来取消订阅。但是,GC.Collect()在这里不太可能有所帮助--对象仍然根植于某个地方,因此无法收集。
希望这为您提供了一条解决根本问题的途径,这首先导致需要使用GC.Collect()。但是,如果没有,当然,有一个工作程序比一个失败的程序更好。在我使用GC.Collect()的任何地方,我都会确保代码有很好的文档记录,说明了需要它的原因(没有异常),以及可靠地再现它所需的确切步骤和数据,以便将来想要删除它的程序员能够确定地知道什么时候这样做是安全的。
发布于 2011-03-09 08:31:07
大多数人会说,使您的代码正确工作比使它快速运行更重要。因此,当您不调用GC.Collect()
时,它在30%的情况下无法工作,这比所有其他问题都要重要。
当然,这就引出了一个更深层次的问题:“为什么要得到OOM错误?是否有更深层次的问题需要解决,而不仅仅是调用GC.Collect()
。
但你发现的关于表演的建议。如果你的应用程序在30%的时间里失败了,你会关心它的性能吗?
发布于 2011-03-09 12:38:44
一般来说,GC.Collect
不应该是必要的。如果映像存在于非托管内存中,那么一定要适当地使用GC.AddMemoryPressure
和GC.RemoveMemoryPressure
。
https://stackoverflow.com/questions/5248838
复制相似问题