虽然我确实理解玩这个功能的严重影响(或者至少是我认为的那样),但我不明白为什么它会成为那些受人尊敬的程序员不会使用的东西之一,即使是那些不喜欢的人甚至不知道它是什么。

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

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

毕竟,这两个阶段从未在时间上相互重叠,GC可能收集到的所有优化和统计数据在这里都没有用......

注意:正如你们中的一些人所指出的那样,.NET可能不是像这样的应用程序的最佳平台,但这超出了这个问题的范围。目的是澄清GC.Collect()调用是否可以改善应用程序的整体行为/性能。我们都同意你做这种事情的情况非常罕见,但话说再次,GC试图猜测并且在大多数情况下做得非常好,但它仍然是猜测。

感谢。

有帮助吗?

解决方案

来自Rico的博客......

  

规则#1

     

别。

     

这真的是最重要的   规则。最公平的说法   GC.Collect()的用法是一个坏主意   我详细介绍了一下   原始发布所以我不会重复   这一切都在这里。那么让我们继续......

     

规则#2

     

考虑调用GC.Collect()   刚刚发生的非经常性事件   这个事件非常有可能   已经造成了很多旧的对象   死。

     

这是一个典型的例子,如果你是   写一个客户端应用程序和你   显示一个非常大而复杂的   具有大量数据关联的表单   用它。您的用户只是   可能与这种形式相互作用   创造一些大型物品......事物   像XML文档,或大型DataSet   或两个。当表单关闭这些   对象已经死了所以GC.Collect()   将收回相关的记忆   跟他们一起......

所以听起来这种情况可能属于规则#2,你知道有很多旧物品已经死亡的时刻,并且它是非经常性的。但是,不要忘记Rico的离别词。

  

规则#1应该胜过规则#2   有力的证据。

测量,测量,测量。

其他提示

如果您在生产代码中调用GC.Collect(),那么您基本上就是宣称您比GC的作者了解更多。情况可能就是这样。然而,它通常不是,因此强烈反对。

那么当你使用.NET中的MS Word或MS Excel等COM对象时呢?在释放COM对象后不调用 GC.Collect ,我们发现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()

那么这是不正确使用垃圾收集器吗?如果是这样,我们如何让Interop对象死掉?此外,如果不打算像这样使用,为什么 GC Collect 方法甚至是 Public

嗯,GC是我与之有爱/恨关系的事情之一。我们过去打破了它通过VistaDB并在博客上发表了这篇文章。他们已经修复了它,但需要很长时间才能从这些事情中获得修复。

GC非常复杂,一个适合所有方法的方法非常非常难以实现这么大的功能。 MS已经做了相当不错的工作,但有时可能会愚弄GC。

一般情况下,你不应该添加 Collect ,除非你知道你刚刚丢弃了大量的内存,它将转到中年危机如果GC现在没有清理它。

您可以使用一系列错误的 GC.Collect 语句搞砸整个机器。对collect语句的需求几乎总是指向更大的底层错误。内存泄漏通常与引用有关,也缺乏对它们如何工作的理解。或者在不需要它的对象上使用 IDisposable ,并在GC上加载更高的负载。

密切关注通过系统性能计数器在GC中花费的时间百分比。如果您在GC中使用20%或更多时间看到您的应用程序,则会出现严重的对象管理问题(或异常使用模式)。您希望始终最小化GC花费的时间,因为它会加快您的整个应用程序。

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

尽管我的答案尽可能完整,但如果你也瞄准那个平台,你也应该在Mono下进行测试。由于它是一个完全不同的实现,它可能会遇到与MS实现完全不同的问题。

有些情况下它很有用,但一般情况下应避免使用。您可以将它与GOTO或骑摩托车进行比较:您可以在需要时进行比较,但是您没有告诉您的朋友。

根据我的经验,从来没有建议在生产代码中调用GC.Collect()。在调试中,是的,它有助于澄清潜在的内存泄漏。 我想我的根本原因是程序员已经编写并优化了GC,比我更聪明,如果我达到了一个我觉得我需要调用GC.Collect()的一点,这是一个线索,我已经离开了路径某处。 在您的情况下,听起来您实际上没有内存问题,只是您担心集合会给您的流程带来什么不稳定性。看到它不会清理仍在使用的物体,并且它能够很快适应上升和下降的要求,我认为你不必担心它。

调用GC.Collect()的最大原因之一就是当你刚刚执行了一个会产生大量垃圾的重要事件时,例如你描述的内容。调用GC.Collect()在这里是一个好主意;否则,GC可能不会理解这是“一次性”事件。

当然,你应该对它进行描述,并亲自看看。

嗯,显然你不应该用非实时垃圾收集的语言编写具有实时要求的代码。

在具有明确定义的阶段的情况下,触发垃圾收集器没有问题。但这种情况极为罕见。问题在于,许多开发人员将尝试使用它来解决货物崇拜风格的问题,并且不加选择地添加它会导致性能问题。

调用GC.Collect()强制CLR执行堆栈遍历,以查看是否可以通过检查引用来真正释放每个对象。如果对象的数量很多,这将影响可伸缩性,并且还已知太频繁地触发垃圾收集。信任CLR并让垃圾收集器在适当的时候自行运行。

在循环中创建图像 - 即使您调用dispose,也不会恢复内存。垃圾收集每次。我从照片处理应用程序的1.7GB内存增加到24MB,性能非常出色。

绝对需要调用GC.Collect。

事实上,我认为调用GC.Collect不是一种非常糟糕的做法 可能存在我们需要的情况。例如,我有一个运行线程的表单,它在数据库中打开不同的表,将BLOB字段中的内容提取到临时文件,加密文件,然后将文件读入二进制流并返回到BLOB另一张表中的字段。

整个操作占用了大量内存,并且不确定表中文件内容的行数和大小。搜索结果 我以前常常得到OutofMemory异常,我认为根据计数器变量定期运行GC.Collect是明智的。我增加一个计数器,当达到指定的级别时,调用GC来收集可能已形成的任何垃圾,并回收由于无法预料的内存泄漏而丢失的任何内存。

在此之后,我认为它运作良好,至少没有异常!!!
我用以下方式打电话:

var obj = /* object utilizing the memory, in my case Form itself */
GC.Collect(GC.GetGeneration(obj ,GCCollectionMode.Optimized).

在.net下,执行垃圾收集所需的时间与非垃圾收集的内容的数量密切相关,而不是与垃圾收集的数量相关。实际上,除非一个对象覆盖 Finalize (显式地,或通过C#析构函数),是 WeakReference 的目标,坐在大对象堆上,或者在某些对象中是特殊的与gc相关的其他方式,唯一能识别它作为对象所在的内存的东西就是存在对它的有根引用。否则,GC的操作类似于从建筑物中取出所有有价值的物品,并对建筑物进行炸毁,在旧建筑物的旧址上建造新的建筑物,并将所有有价值的物品放入其中。炸毁建筑物所需的努力完全独立于其中的垃圾量。

因此,调用 GC.Collect 很容易增加系统必须完成的工作量。它会延迟下一个集合的发生,但可能会在下一个集合发生时立即执行同样多的工作;在下一次收集发生时,收集的总时间与 GC.Collect 未被调用的时间大致相同,但系统会积累一些垃圾,导致比没有调用 GC.Collect 更快要求的后续集合。

我可以看到 GC.Collect 真正有用的时候是需要测量某些代码的内存使用量(因为内存使用数字只是在集合后才真正有意义),或者配置文件几种算法中的哪一种更好(在运行几段代码之前调用GC.Collect()可以帮助确保一致的基线状态)。在其他一些情况下,人们可能知道GC没有的东西,但除非有人正在编写单线程程序,否则无法知道 GC.Collect 调用哪个会有所帮助一个线程的数据结构避免了“中年危机”。不会导致其他线程的数据产生“中年危机”否则就会被避免。

我们遇到了类似的问题,垃圾收集器没有收集垃圾和释放内存。

在我们的程序中,我们使用OpenXML处理一些中等大小的Excel Spreadsheets。电子表格包含5到10个“片材”的任何地方。大约1000行14列。

32位环境(x86)中的程序会因“内存不足”而崩溃。错误。我们确实让它在x64环境中运行,但我们想要一个更好的解决方案。

我们找到了一个。

以下是一些简化的代码片段,其中包含了什么不起作用以及在显式调用垃圾收集器以释放已处置对象的内存时所起的作用。

从子程序内部调用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垃圾收集感到沮丧的人。

保罗史密斯

显式调用集合没有任何问题。有些人真的想要相信如果它是供应商提供的服务,不要质疑它。哦,所有这些随机冻结在交互式应用程序的错误时刻?下一个版本会让它变得更好!

让后台进程处理内存操作意味着不必自己处理它,这是真的。但这在逻辑上并不意味着我们最好不要在任何情况下自己处理它。 GC针对大多数情况进行了优化。但这在逻辑上并不意味着它在所有情况下都得到了优化。

你有没有回答一个开放的问题,比如'哪个是最好的排序算法',并且有明确的答案?如果是这样,请勿触摸GC。对于那些询问条件或给出“在这种情况下”类型答案的人,您可以继续了解GC以及何时激活它。

要说,我在Chrome和Firefox中冻结应用程序会让我感到沮丧,即便如此,在某些情况下,内存也会不受阻碍地增加 - 如果只是他们学会调用垃圾收集器 - 或者给我一个按钮,这样当我开始阅读页面文本时,我可以点击它,因此在接下来的20分钟内不会冻结。

我认为你的情况是正确的,但我不确定API。

微软表示,在这种情况下你应该增加内存压力作为GC的提示,它应该很快执行收集。

它出了什么问题?你是第二次猜测垃圾收集器和内存分配器的事实,它们之间对你的应用程序在运行时的实际内存使用情况有了更多的了解。

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

如果你发现忘记处理你不再需要的东西会更好。

从底线,您可以分析应用程序并查看这些额外的集合如何影响事物。除非你要描述,否则我建议远离它。 GC旨在照顾自己,随着运行时的发展,它们可以提高效率。你不希望一堆代码徘徊,可能会破坏工作,而不能利用这些改进。使用foreach而不是for,有一个类似的论点,即封面下的未来改进可以添加到foreach中,并且您的代码不必更改以利用。

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

最糟糕的是让程序冻结一点。所以,如果你没事,那就去做吧。通常,大多数用户交互的胖客户端或Web应用程序都不需要它。

我发现有时程序包含长时间运行的线程或批处理程序,即使它们正确处理对象,也会出现OutOfMemory异常。我记得一个是业务线数据库事务处理;另一个是胖客户端应用程序中后台线程的索引例程。

在这两种情况下,结果很简单:没有GC.Collect,内存不足,始终如一; GC.Collect,完美无瑕的表现。

我已经尝试过几次解决内存问题,但无济于事。我拿出来了。

简而言之,除非你遇到错误,否则不要把它放进去。如果你把它放入并且它不能解决内存问题,请将其取出。记得在发布模式下测试并比较苹果和苹果。

唯一可能出现问题的事情就是当你对此有道德时。这不是一个价值问题;许多程序员已经死了,直接进入天堂,有许多不必要的GC.Collects在他们的代码中,它们比他们更长。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top