当前位置:  开发笔记 > 编程语言 > 正文

何时可以调用GC.Collect?

如何解决《何时可以调用GC.Collect?》经验,为你挑选了10个好方法。

一般建议您不应该GC.Collect从您的代码中调用,但此规则的例外情况是什么?

我只能想到一些非常具体的案例,强制垃圾收集可能是有意义的.

想到的一个例子是服务,每隔一段时间醒来,执行一些任务,然后长时间睡眠.在这种情况下,强制收集以防止即将空闲的进程保持比需要的更多内存可能是个好主意.

是否还有其他可以接听电话的情况GC.Collect



1> Jon Skeet..:

如果您有充分的理由相信一组重要的对象 - 特别是那些您怀疑属于第1代和第2代的对象 - 现在有资格进行垃圾收集,那么现在可能是收集小型性能的合适时间.

一个很好的例子就是你刚刚关闭了一个大表格.您知道所有UI控件现在都可以进行垃圾回收,并且表单关闭时非常短暂的停顿可能对用户来说不太明显.

更新2.7.2018

从.NET 4.5开始 - 有GCLatencyMode.LowLatencyGCLatencyMode.SustainedLowLatency.当进入和离开这些模式中的任何一种时,建议您强制使用完整的GC GC.Collect(2, GCCollectionMode.Forced).

从.NET 4.6开始 - 有GC.TryStartNoGCRegion方法(用于设置只读值GCLatencyMode.NoGCRegion).这本身可以执行完全阻塞垃圾收集以试图释放足够的内存,但鉴于我们不允许使用GC一段时间,我认为在之前和之后执行完整GC也是一个好主意.

资料来源:微软工程师Ben Watson:编写高性能.NET代码,第二版.2018.

看到:

https://msdn.microsoft.com/en-us/library/system.runtime.gclatencymode(v=vs.110).aspx

https://msdn.microsoft.com/en-us/library/dn906204(v=vs.110).aspx


@ springy76:微软在一个地方完成并不意味着那些来自*微软提供建议*的人认为这是件好事......
根据MS源代码调用GC.Collect(2)每850ms就好了.不信?然后只看到PresentationCore.dll,MS.Internal.MemoryPressure.ProcessAdd().我目前有一个图像处理应用程序(小图像,没有真正的内存压力),其中调用GC.Collect(2)需要超过850毫秒,因此整个应用程序被冻结(应用程序花费99.7%的时间在GC).
@Peri:关闭表单之后做的就是你刚刚制作了一堆对象(控件,你显示的数据)有资格进行垃圾收集 - 所以通过调用`GC.Collect`你基本上是在说垃圾收集器,你知道比更改更好.你为什么不喜欢这个例子?
@SHCJ:`GC.Collect()`将请求GC执行*full*集合.如果你知道你刚刚制作了许多以前长寿命的对象,有资格进行垃圾收集*和*你认为用户现在不太可能注意到暂时的暂停,那么认为现在是完全合理的提示收集的最佳时间比让以后发生收集更好.
我不喜欢那个例子.关闭之后,这样做的意义何在?我看到的一个很好的例子是在XBox或WindowsPhone上加载游戏关卡后.在这些平台上,GC在分配1MB或类似的情况下运行.因此,在加载关卡期间尽可能多地分配(同时显示一些启动画面),然后执行GC.Collect以尝试避免游戏中的收藏.
我只是有一个案例,我"需要"调用`GC.Collect()`:我对图像进行批量处理,我在其中加载图像,将其调整为两个不同大小的图像,然后将它们保存到磁盘:Even在所有`Image`实例周围使用`using`-statements,GC花了几秒钟清除旧对象 - >> 500MB RAM"不必要"使用,因为实际处理快速填充它 - >调用后` GC.Collect()`在每个处理过的图像之后没有显着降低过程的速度,但是RAM的使用率在几MB时是稳定的......

2> Marc Gravell..:

GC.Collect只在编写原始性能/分析器测试装置时使用; 即我有两个(或更多)代码块来测试 - 类似于:

GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
TestA(); // may allocate lots of transient objects
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
TestB(); // may allocate lots of transient objects
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
...

所以这TestA()TestB()与类似状态下运行-即TestB()未得到公正敲定,因为TestA离开它非常接近临界点.

一个典型的例子是一个简单的控制台exe(一种Main方法排序 - 足以在这里发布),它显示了循环字符串连接和StringBuilder.

如果我需要精确的东西,那么这将是两个完全独立的测试 - 但是如果我们只想在测试期间最小化(或标准化)GC以获得对行为的粗略感觉,这通常就足够了.

在生产代码期间?我还没有用它;-p



3> Ian Ringrose..:

最佳做法是在大多数情况下不强制进行垃圾收集. (我所研究的每个系统都强制进行垃圾收集,强调了问题,如果解决这个问题就会消除强制垃圾收集的需要,并大大加快了系统的速度.)

在某些情况下,您可以了解有关内存使用情况的更多信息.在多用户应用程序或一次响应多个请求的服务中,这不可能成立.

但是在某些批处理类型处理中,您确实了解GC.例如,考虑一个应用程序.

在命令行上给出了文件名列表

处理单个文件,然后将结果写入结果文件.

处理文件时,会创建大量互连对象,在文件处理完成之前无法收集这些对象(例如,解析树)

不保持它已处理的文件之间的匹配状态.

可能能够(经过仔细测试)测试后应该在处理完每个文件后强制执行完整的垃圾回收.

另一种情况是每隔几分钟醒来处理一些物品,并且在睡着时不保持任何状态的服务.然后在睡觉之前强制完整收集可能是值得的.

我唯一一次考虑强制收集的是当我知道最近创建了很多对象时,当前引用的对象非常少.

我宁愿有一个垃圾收集API,当我可以给它提示这种类型的东西,而不必强迫我自己的GC.

另见" Rico Mariani的表演花絮 "



4> Brian..:

一种情况是当您尝试使用WeakReference对单元测试代码进行单元化时.



5> Joel Coehoor..:

当你知道垃圾收集器没有的应用程序的性质时,你可以调用GC.Collect().很有可能认为,作为作者,这很有可能.然而,事实是GC相当于一个写得很好并且经过测试的专家系统,而且你很少知道它没有的低级代码路径.

我能想到你可能有一些额外信息的最佳例子是一个在空闲时段和非常繁忙的时段之间循环的应用程序.您希望在繁忙时段获得最佳性能,因此希望使用空闲时间进行一些清理.

但是,大多数情况下,GC无论如何都足够聪明.



6> Paul Ruane..:

在大型24/7或24/6系统中 - 对消息,RPC请求或连续轮询数据库或进程的系统作出反应的系统 - 有一种识别内存泄漏的方法很有用.为此,我倾向于为应用程序添加一种机制,以暂时挂起任何处理,然后执行完整的垃圾收集.这使系统处于静止状态,其中剩余的内存是合法的长期存储器(缓存,配置等)或者是"泄漏"(不期望或希望被植根但实际上是对象的对象).

使用此机制可以更轻松地分析内存使用情况,因为报告不会受到活动处理噪音的影响.

为了确保获得所有垃圾,您需要执行两个集合:

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

由于第一个集合将导致具有终结器的任何对象被最终化(但实际上并不垃圾收集这些对象).第二个GC将垃圾收集这些已完成的对象.



7> 小智..:

作为内存碎片解决方案.在将大量数据写入内存流(从网络流中读取)时,我遇到了内存不足的问题.数据以8K块的形式写成.达到128M后,即使有大量可用内存(但它是碎片化的),也有异常.调用GC.Collect()解决了这个问题.修复后我能够处理超过1G.



8> M4N..:

看看Rico Mariani撰写的这篇文章.他在调用GC.Collect时给出两条规则(规则1是:"不要"):

何时调用GC.Collect()


已经去过那里了。我不是要为做不该做的事情找借口,但我想知道是否有任何特定的情况可以接受。

9> 小智..:

通过Interop自动化Microsoft Office时,几乎需要调用GC.Collect()的一个实例。Office的COM对象不喜欢自动释放,并且可能导致Office产品实例占用大量内存。我不确定这是问题还是设计使然。互联网上有很多关于此主题的文章,因此我不会赘述。

使用Interop进行编程时,通常应通过使用Marshal.ReleseComObject()来手动释放每个 COM对象。另外,手动调用垃圾回收可以帮助“清理”。使用完Interop对象后,调用以下代码似乎会有所帮助:

GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()

以我个人的经验,结合使用ReleaseComObject和手动调用垃圾回收会大大减少Office产品(特别是Excel)的内存使用量。



10> Daniel B..:

我正在对数组和列表进行一些性能测试:

private static int count = 100000000;
private static List GetSomeNumbers_List_int()
{
    var lstNumbers = new List();
    for(var i = 1; i <= count; i++)
    {
        lstNumbers.Add(i);
    }
    return lstNumbers;
}
private static int[] GetSomeNumbers_Array()
{
    var lstNumbers = new int[count];
    for (var i = 1; i <= count; i++)
    {
        lstNumbers[i-1] = i + 1;
    }
    return lstNumbers;
}
private static int[] GetSomeNumbers_Enumerable_Range()
{
    return  Enumerable.Range(1, count).ToArray();
}

static void performance_100_Million()
{
    var sw = new Stopwatch();

    sw.Start();
    var numbers1 = GetSomeNumbers_List_int();
    sw.Stop();
    //numbers1 = null;
    //GC.Collect();
    Console.WriteLine(String.Format("\"List\" took {0} milliseconds", sw.ElapsedMilliseconds));

    sw.Reset();
    sw.Start();
    var numbers2 = GetSomeNumbers_Array();
    sw.Stop();
    //numbers2 = null;
    //GC.Collect();
    Console.WriteLine(String.Format("\"int[]\" took {0} milliseconds", sw.ElapsedMilliseconds));

    sw.Reset();
    sw.Start();
//getting System.OutOfMemoryException in GetSomeNumbers_Enumerable_Range method
    var numbers3 = GetSomeNumbers_Enumerable_Range();
    sw.Stop();
    //numbers3 = null;
    //GC.Collect();

    Console.WriteLine(String.Format("\"int[]\" Enumerable.Range took {0} milliseconds", sw.ElapsedMilliseconds));
}

我得到OutOfMemoryException了GetSomeNumbers_Enumerable_Range方法,唯一的解决方法是通过以下方式释放内存:

numbers = null;
GC.Collect();

推荐阅读
路人甲
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有