根据我的经验,似乎大多数人会告诉你强制垃圾收集是不明智的,但在某些情况下,你正在使用大型对象,这些对象并不总是在0代收集但内存是问题,强制收集是否可以?这样做是否有最好的做法?
答案 0 :(得分:110)
最佳做法是不强制进行垃圾回收。
根据MSDN:
“可以强迫垃圾 通过调用Collect收集,但是 大部分时间,这应该是 避免因为它可能会创造 性能问题。 “
但是,如果您可以可靠地测试您的代码以确认调用Collect()不会产生负面影响,那么请继续...
尝试确保在不再需要时清理对象。如果您有自定义对象,请查看“using statement”和IDisposable接口。
这个链接在释放内存/垃圾收集等方面有一些很好的实用建议:
答案 1 :(得分:30)
最佳做法是在大多数情况下不强制进行垃圾收集。(我曾经处理的每个系统都强制进行垃圾收集,强调了问题,如果解决这个问题就会消除强迫的需要垃圾收集,大大加快了系统。)
当你了解有关内存使用情况的更多内容时,少数情况然后垃圾收集器会这样做。在多用户应用程序或一次响应多个请求的服务中,这种情况不太可能发生。
然而,在某些批处理类型处理中,您确实了解GC。例如。考虑一个应用程序。
另一种情况是每隔几分钟醒来处理一些项目的服务,在睡着时不保持任何状态。然后在睡觉之前强制完整收集可能值得。
我唯一一次考虑强迫 一个集合就是我知道的很多 最近创建了对象 目前很少有物品 引用。
我宁愿拥有一个垃圾收集API,当我能给它提示这种类型的东西,而不必强迫GC自己。
答案 2 :(得分:28)
以这种方式看待它 - 当垃圾桶达到10%时扔掉厨房垃圾还是让它在装满之前填满它是否更有效?
不要让它填满,你浪费时间往外走垃圾桶。这类似于GC线程运行时发生的情况 - 所有托管线程在运行时都被挂起。如果我没有弄错的话,GC线程可以在多个AppDomain之间共享,因此垃圾收集会影响所有这些。
当然,你可能会遇到这样一种情况:你不会很快在垃圾桶里添加任何东西 - 比方说,如果你要休假。然后,在外出之前把垃圾丢掉是个好主意。
这可能是迫使GC有所帮助的一次 - 如果您的程序空闲,则使用的内存不会被垃圾收集,因为没有分配。
答案 3 :(得分:20)
我认为Rico Mariani给出的示例很好:如果应用程序的状态发生重大变化,可能适合触发GC。例如,在文档编辑器中,可以在文档关闭时触发GC。
答案 4 :(得分:15)
编程中很少有一般指导方针是绝对的。有一段时间,当有人说'你做错了'时,他们只是在喷出一定量的教条。在C语言中,过去常担心自修改代码或线程,在GC语言中它强制GC或者阻止GC运行。
与大多数指南和良好的经验法则(以及良好的设计实践)一样,在极少数情况下,围绕既定规范工作是有意义的。你必须非常确定你理解这个案子,你的案件确实需要废除普通的做法,并且你了解你可能导致的风险和副作用。但是有这样的情况。
编程问题种类繁多,需要灵活的方法。我已经看到了在垃圾收集语言中阻止GC以及触发它而不是等待它自然发生的地方的情况。 95%的时间,其中任何一个都是没有正确处理问题的路标。但是在20年的一次,可能会有一个有效的案例。
答案 5 :(得分:13)
我已经学会了不要试图超越垃圾收集。话虽如此,我只是在处理非托管资源(如文件I / O或数据库连接)时坚持使用 using
关键字。
答案 6 :(得分:9)
不确定这是否是最佳做法,但在循环中处理大量图像时(即创建和处理大量图形/图像/位图对象),我经常让GC.Collect。
我想我在某处看到GC只在程序(大部分)空闲时运行,而不是在密集循环的中间运行,因此看起来像手动GC可能有意义的区域。
答案 7 :(得分:9)
我最近遇到过一个需要手动调用GC.Collect()
的情况,就是在使用包含在很小的托管C ++对象中的大型C ++对象时,这些对象又是从C#访问的。
垃圾收集器从未被调用,因为使用的托管内存量可以忽略不计,但是使用的非托管内存量非常大。在对象上手动调用Dispose()
将要求我跟踪自己何时不再需要对象,而调用GC.Collect()
将清除任何不再引用的对象.....
答案 8 :(得分:7)
我认为你已经列出了最佳实践,除非真的需要,否则不要使用它。我强烈建议您更详细地查看您的代码,如果需要首先使用分析工具来回答这些问题。
答案 9 :(得分:5)
假设您的程序没有内存泄漏,对象累积且无法在Gen 0中进行GC编辑,因为: 1)它们被长时间引用,因此进入Gen1&第二代; 2)它们是大对象(> 80K),因此进入LOH(大对象堆)。并且LOH不像Gen0,Gen1和amp;中那样进行压缩。第二代。
检查“.NET内存”的性能计数器可以看出1)问题确实不是问题。通常,每10个Gen0 GC将触发1个Gen1 GC,每10个Gen1 GC将触发1个Gen2 GC。理论上,GC1&如果GC0上没有压力(如果程序存储器使用情况确实有线),则GC2永远不能进行GC编辑。它永远不会发生在我身上。
对于问题2),您可以检查“.NET Memory”性能计数器以验证LOH是否变得臃肿。如果这确实是您的问题的一个问题,也许您可以创建一个大对象池,因为此博客建议http://blogs.msdn.com/yunjin/archive/2004/01/27/63642.aspx。
答案 10 :(得分:4)
大对象在LOH(大对象堆)上分配,而不是在gen 0上。如果你说他们没有使用gen 0进行垃圾收集,那么你是对的。我相信只有在完整的GC循环(第0代,第1代和第2代)发生时才收集它们。
话虽这么说,我相信另一方面,当您处理大型物体并且内存压力上升时,GC会更积极地调整和收集内存。
很难说是否收集以及在何种情况下收集。在处理具有大量控件等的对话框窗口/表单之后,我曾经做GC.Collect()(因为当表格及其控件由于创建业务对象的多个实例/加载大量数据而在第2代结束时 - 很明显是大型物体,但实际上并没有注意到长期的任何积极或消极影响。
答案 11 :(得分:4)
我想补充一点: 调用GC.Collect()(+ WaitForPendingFinalizers())是故事的一部分。 正如其他人正确提到的那样,GC.COllect()是非确定性的集合,由GC本身(CLR)自行决定。 即使您添加对WaitForPendingFinalizers的调用,它也可能不是确定性的。 从这个msdn link获取代码,并以对象循环迭代为1或2运行代码。您将找到非确定性方法(在对象的析构函数中设置断点)。 确切地说,当Wait ...()中只有1个(或2个)延迟对象时,不会调用析构函数。[Citation reqd。]
如果您的代码处理非托管资源(例如:外部文件句柄),则必须实现析构函数(或终结器)。
这是一个有趣的例子:
注意:如果您已经在MSDN上尝试了上述示例,则以下代码将清除空气。
class Program
{
static void Main(string[] args)
{
SomePublisher publisher = new SomePublisher();
for (int i = 0; i < 10; i++)
{
SomeSubscriber subscriber = new SomeSubscriber(publisher);
subscriber = null;
}
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine(SomeSubscriber.Count.ToString());
Console.ReadLine();
}
}
public class SomePublisher
{
public event EventHandler SomeEvent;
}
public class SomeSubscriber
{
public static int Count;
public SomeSubscriber(SomePublisher publisher)
{
publisher.SomeEvent += new EventHandler(publisher_SomeEvent);
}
~SomeSubscriber()
{
SomeSubscriber.Count++;
}
private void publisher_SomeEvent(object sender, EventArgs e)
{
// TODO: something
string stub = "";
}
}
我建议,首先分析输出然后运行,然后阅读以下原因:
{只有在程序结束时才会隐式调用析构函数。 } 为了确定性地清理对象,必须实现IDisposable并显式调用Dispose()。这就是本质! :)
答案 12 :(得分:2)
另外一件事,明确触发GC Collect可能无法提高程序的性能。很可能会让情况变得更糟。
.NET GC经过精心设计和调整为自适应,这意味着它可以根据程序内存使用的“习惯”调整GC0 / 1/2阈值。因此,它会在运行一段时间后适应您的程序。一旦显式调用GC.Collect,阈值将被重置! .NET必须花时间再次适应你的程序的“习惯”。
我的建议总是信任.NET GC。任何内存问题浮出水面,检查“.NET内存”性能计数器并诊断我自己的代码。
答案 13 :(得分:1)
不确定这是否是最佳做法......
建议:不确定时不要实现这个或任何东西。在知道事实时重新评估,然后在性能测试之前/之后执行以进行验证。
答案 14 :(得分:0)
但是,如果您可以可靠地测试您的代码以确认调用Collect()不会产生负面影响,那么请继续...
恕我直言,这类似于说“如果你能证明你的程序将来不会有任何错误,那就继续......”
严肃地说,强制GC对于调试/测试非常有用。如果您觉得在其他任何时候都需要这样做,那么您错了,或者您的程序构建错误。无论哪种方式,解决方案都没有强制GC ......