使用GC.Collect()有什么错误?
虽然我理解玩这个函数的严重含义(或者至less我认为是这样),但是我不明白为什么它会成为可敬的程序员永远不会使用的东西之一,即使是那些甚至不知道它是什么。
比方说,我正在开发一个应用程序,内存使用情况因使用者的行为而异。 应用程序生命周期可以分为两个主要阶段:编辑和实时处理。 在编辑阶段,假设创build了数十亿甚至数万亿个对象, 其中一些很小,有些则不是,有些可能有终结者,有些可能不是,并假设他们的生命期从几毫秒到长时间不等。 接下来,用户决定切换到实时阶段。 在这一点上,假设演出起着基础性的作用,而节目stream程中稍有改变就可能带来灾难性的后果。 然后通过使用对象池将对象创build减less到最小,但是,然后,GC出乎意料地响起,将其全部抛出,并且有人死亡。
问题:在这种情况下,在进入第二阶段之前调用GC.Collect()是否明智?
毕竟,这两个阶段从来没有互相重叠过,GC可能收集到的所有优化和统计数据都没有什么用处。
注意:正如你们中的一些人所指出的,.NET可能不是像这样的应用程序的最佳平台,但这超出了这个问题的范围。 目的是澄清GC.Collect()调用是否可以提高应用程序的整体行为/性能。 我们都同意,在这种情况下你会做这样的事情是非常罕见的,但是GC再次尝试猜测,大部分时间都做得很好,但这仍然是猜测。
谢谢。
来自Rico的博客…
规则1
别。
这真的是最重要的规则。 可以这么说,GC.Collect()的大多数用法是一个糟糕的主意,我在前一篇文章中详细介绍了这一点,所以在这里我不再重复。 那么让我们继续…
规则#2
考虑调用GC.Collect(),如果一些非循环事件刚刚发生,并且这个事件很可能已经导致大量的旧对象死亡。
一个典型的例子是如果你正在编写一个客户端应用程序,并且显示一个非常庞大而复杂的表单,并且有很多与之相关的数据。 你的用户刚刚与这个表单交互,可能会创build一些大的对象…比如XML文档,或者一个大的DataSet或者两个。 当表单closures时,这些对象是死的,所以GC.Collect()将回收与它们相关的内存。
所以听起来这样的情况可能会在规则#2下出现,你知道有很多旧对象已经死亡的时刻,而且是不经常发生的。 但是,别忘了里科的离别词。
规则#1应该在没有有力证据的情况下胜过规则#2。
衡量,衡量,衡量。
如果你在产品代码中调用GC.Collect(),你基本上宣称你知道更多GC的作者。 情况可能如此。 但是通常不会,因此强烈不鼓励。
那么如何使用.NET中的MS Word或MS Excel等COM对象呢? 没有调用GC.Collect
释放COM对象,我们发现Word或Excel应用程序实例仍然存在。
实际上我们使用的代码是:
Utils.ReleaseCOMObject(objExcel) ' Call the Garbage Collector twice. The GC needs to be called twice in order to get the ' Finalizers called - the first time in, it simply makes a list of what is to be finalized, ' the second time in, it actually does the finalizing. Only then will the object do its ' automatic ReleaseComObject. Note: Calling the GC is a time-consuming process, ' but one that may be necessary when automating Excel because it is the only way to ' release all the Excel COM objects referenced indirectly. ' Ref: http://www.informit.com/articles/article.aspx?p=1346865&seqNum=5 ' Ref: http://support.microsoft.com/default.aspx?scid=KB;EN-US;q317109 GC.Collect() GC.WaitForPendingFinalizers() GC.Collect() GC.WaitForPendingFinalizers()
那么这将是垃圾收集器的不正确使用? 如果是的话,我们如何让互操作对象死亡? 另外,如果不是这样使用,为什么GC
的Collect
方法甚至是Public
?
那么,GC是我与爱情/仇恨关系中的一个。 我们已经通过VistaDB 打破了它,并且在博客上发表了这篇文章。 他们已经解决了这个问题,但是从这些事情上得到修复需要很长的时间。
GC是复杂的,一个适合所有的方法是非常非常非常困难的东西这样大的东西。 MS已经做得相当好了,但有时候可能会愚弄GC。
一般来说,除非你知道一个事实,否则你不应该添加一个Collect
如果GC没有清理干净的话,它就会陷入中年危机 。
你可以用一系列糟糕的GC.Collect
语句搞砸整个机器。 收集声明的需要几乎总是指向更大的潜在错误。 内存泄漏通常与引用有关,对于它们的工作方式缺乏理解。 或者在不需要的物体上使用IDisposable
,并在GC上施加更高的负荷。
仔细观察通过系统性能计数器在GC中花费的时间百分比。 如果您在GC中使用了20%或更多的应用程序,那么您的应用程序出现严重的对象pipe理问题(或使用模式exception)。 您希望始终最小化GC花费的时间,因为它会加速您的整个应用程序。
同样重要的是要注意,GC在服务器上与工作站不同。 我看到一些小的人很难跟踪没有testing他们两个人的问题(甚至没有意识到他们是两个人)。
只要我的答案尽可能完整,你也应该在单声道下进行testing,如果你也是针对这个平台的话。 由于这是一个完全不同的实现,它可能会遇到与MS实现完全不同的问题。
根据我的经验,在生产代码中调用GC.Collect()是不可取的。 在debugging中,是的,这有助于澄清潜在的内存泄漏。 我想我的根本原因是GC已经被程序员编写和优化了,比我更聪明,如果我觉得我需要调用GC.Collect(),这是我走了一条路某处。 在你的情况下,这听起来不像是你真的有内存问题,只是你担心收集会给你的过程带来什么样的不稳定性。 看到它不会清理仍在使用的物体,而且它很快适应升降要求,我想你不必担心。
在有些情况下,它是有用的,但总的来说应该避免。 你可以把它比作GOTO,或是骑摩托车:当你需要的时候就做,但是不要告诉你的朋友。
那么,显然你不应该用具有非实时垃圾收集的语言编写具有实时需求的代码。
在阶段明确的情况下,触发垃圾收集器没有问题。 但是这种情况非常罕见。 问题在于,很多开发商会试图用这种方法来解决货物风险问题,如果不加区分地添加,就会造成性能问题。
调用GC.Collect()的最大原因之一就是当你刚刚执行了一个重要的事件时,会产生大量垃圾,比如你描述的内容。 调用GC.Collect()在这里是一个好主意。 否则,GC可能不了解这是“一次性”事件。
当然,你应该介绍一下,亲自看看。
调用GC.Collect()强制CLR执行堆栈遍历,以查看是否可以通过检查引用真正释放每个对象。 如果对象数量很多,这将影响可伸缩性,并且也被认为会经常触发垃圾收集。 相信CLR,让垃圾收集器在适当的时候运行。
在.net下,执行垃圾回收所需的时间与非垃圾回收的数量相关,而不是垃圾回收的数量。 事实上,除非一个对象重写Finalize
(显式地或者通过C#析构函数),否则WeakReference
的目标位于大对象堆中,或者在其他一些与gc有关的方法中是特殊的,唯一标识内存的它作为一个对象是存在根源的参考。 否则,地理信息系统的运作就类似于从build筑物中取出一切有价物品,将build筑物炸毁,在旧build筑物上build造一个新的build筑物,并把所有有价值的物品放在里面。 炸毁build筑物所需的努力完全独立于其内的垃圾量。
因此,调用GC.Collect
可能会增加系统的总体工作量。 它会延迟下一次收集的发生,但可能会立即完成与下一次收集发生时一样多的工作。 在下次收集的时候,收集的总时间与GC.Collect
没有被调用的总时间大致相同,但是系统会累积一些垃圾,导致需要更早的收集比GC.Collect
没有被调用。
我可以看到GC.Collect
真正有用的时代是需要测量某些代码的内存使用情况(因为内存使用量数据在收集之后才真正有意义),或者几个algorithm中的哪一个algorithm更好(调用GC)。在运行几段代码之前收集()可以帮助确保一致的基线状态)。 还有其他一些情况,可能知道GC没有的东西,但除非有人编写单线程程序,否则无法知道GC.Collect
调用会帮助一个线程的数据结构避免“中间“生命危机”不会导致其他线程的数据产生本来可以避免的“中年危机”。
事实上,我不认为这是一个非常糟糕的做法,调用GC.Collect。
有些情况下,我们需要这样做。 举个例子,我有一个运行线程的表单,它会打开数据库中的不同表,将BLOB字段中的内容提取到临时文件,对文件进行encryption,然后将文件读入二进制stream并返回到BLOB在另一个表中的字段。
整个操作占用了大量的内存,并且不能确定表中文件内容的行数和大小。
我经常经常使用OutofMemoryexception,我认为定期运行基于计数器variables的GC.Collect是明智的。 我增加一个计数器,当达到指定的级别时,调用GC来收集可能已经形成的垃圾,并回收由于不可预见的内存泄漏而丢失的任何内存。
在这之后,我认为它运作良好,至less也不例外!
我以下面的方式打电话:
GC.Collect(GC.GetGeneration(<object utilizing the memory> /* in mycase Form itself */,GCCollectionMode.Optimized).
在循环中创build图像 – 即使您调用dispose,也不会恢复内存。 垃圾收集每一次。 我从我的照片处理应用程序1.7GB的内存去24MB,性能非常好。
有绝对的时间,你需要调用GC.Collect。
显式调用集合没有任何问题。 有些人只是真的想相信,如果这是一个供应商提供的服务,不要质疑它。 哦,所有这些随机冻结在你的交互式应用程序的错误时刻? 下一个版本会更好!
让后台进程处理内存操作意味着不必自己处理它,这是真的。 但这在逻辑上并不意味着在任何情况下我们最好不要自己处理。 GC针对大多数情况进行了优化。 但这在逻辑上并不意味着在所有情况下都进行了优化。
你有没有回答一个开放的问题,如“哪个是最好的sortingalgorithm”,并给出明确的答案? 如果是这样,请勿触摸GC。 对于那些询问条件的人,或者在这种情况下给出的types答案,你可以继续学习GC,以及何时激活它。
要说,我已经在Chrome和Firefox中冻结了应用程序,这让我很难过,即使如此,在某些情况下,内存不受阻碍地增长 – 如果他们只学习调用垃圾收集器 – 或者给我一个button,当我开始阅读一个页面的文本,我可以打它,因此在接下来的20分钟内没有冻结。
我认为你的情况是正确的,但我不确定的API。
微软表示,在这种情况下,你应该增加内存压力,作为一个暗示GC,它应该马上执行一个集合。
它出什么问题了? 事实上,你是在猜测垃圾收集器和内存分配器,它们之间有比你更多的关于你的应用程序在运行时的实际内存使用情况的想法。
调用GC.Collect()的愿望通常是试图掩盖你在其他地方犯的错误!
如果你发现你忘了处理你不再需要的东西,那会更好。
垃圾收集器没有收集垃圾,释放内存也有类似的问题。
在我们的程序中,我们正在使用OpenXML处理一些规模适中的Excel电子表格。 电子表格包含5到10个“表单”,大约1000行,每列14列。
在32位环境(x86)中的程序将崩溃,出现“内存不足”错误。 我们确实得到它在x64环境中运行,但我们想要一个更好的解决scheme。
我们find了一个。
以下是一些简化的代码片段,说明什么不起作用,以及在显式调用垃圾收集器以释放处置对象的内存时工作。
从子程序内调用GC不起作用。 内存从来没有回收…
For Each Sheet in Spreadsheets ProcessSheet(FileName,sheet) Next Private Sub ProcessSheet(ByVal Filename as string, ByVal Sheet as string) ' open the spreadsheet Using SLDoc as SLDocument = New SLDocument(Filename, Sheet) ' do some work.... SLDoc.Save End Using GC.Collect() GC.WaitForPendingFinalizers() GC.Collect() GC.WaitForPendingFinalizers() End Sub
通过将GC调用移动到子例程的范围之外,垃圾被收集,内存被释放。
For Each Sheet in Spreadsheets ProcessSheet(FileName,sheet) GC.Collect() GC.WaitForPendingFinalizers() GC.Collect() GC.WaitForPendingFinalizers() Next Private Sub ProcessSheet(ByVal Filename as string, ByVal Sheet as string) ' open the spreadsheet Using SLDoc as SLDocument = New SLDocument(Filename, Sheet) ' do some work.... SLDoc.Save End Using End Sub
我希望这可以帮助那些无视GC.Collect()
调用的.NET垃圾回收机制。
保罗史密斯
底线,你可以剖析应用程序,看看这些额外的集合如何影响的东西。 我build议远离它,虽然除非你要configuration文件。 GC被devise成照顾自己,随着运行时间的发展,他们可能会提高效率。 你不想要一堆代码,可能会把工作搞糊涂,无法利用这些改进。 对于使用foreach有一个类似的说法,而不是为了,可以在foreach中添加未来的改进,并且您的代码不必更改以利用。
.NET框架本身从未被devise为在实时环境中运行。 如果您确实需要实时处理,则可以使用不基于.NET的embedded式实时语言,也可以使用在Windows CE设备上运行的.NET Compact Framework。
最糟糕的是让你的程序冻结一下。 所以,如果你没有问题,就去做吧。 通常对于大多数用户交互的胖客户端或networking应用程序来说是不需要的。
我发现有时程序长时间运行的线程或批处理程序,即使正确处理对象,也会出现OutOfMemoryexception。 我记得一个业务线数据库事务处理; 另一个是在厚客户端应用程序中的后台线程上的索引例程。
在这两种情况下,结果都很简单:没有GC.Collect,内存不足,始终如一; GC.Collect,performance完美无瑕。
我已经尝试过几次解决内存问题,但都无济于事。 我拿出来了
总之,不要把它放进去,除非你得到错误。 如果你把它放进去,它不能解决内存问题,把它拿回来。 请记住在发布模式下进行testing,并将苹果与苹果进行比较。
唯一可能出现的问题就是当你得到道德的时候。 这不是一个价值问题。 许多程序员已经死了,直接上了许多不必要的GC。收集他们的代码,它们超过了他们。