一般建议您不应该GC.Collect
从您的代码中调用,但此规则的例外情况是什么?
我只能想到一些非常具体的案例,强制垃圾收集可能是有意义的.
想到的一个例子是服务,每隔一段时间醒来,执行一些任务,然后长时间睡眠.在这种情况下,强制收集以防止即将空闲的进程保持比需要的更多内存可能是个好主意.
是否还有其他可以接听电话的情况GC.Collect
?
如果您有充分的理由相信一组重要的对象 - 特别是那些您怀疑属于第1代和第2代的对象 - 现在有资格进行垃圾收集,那么现在可能是收集小型性能的合适时间.
一个很好的例子就是你刚刚关闭了一个大表格.您知道所有UI控件现在都可以进行垃圾回收,并且表单关闭时非常短暂的停顿可能对用户来说不太明显.
更新2.7.2018
从.NET 4.5开始 - 有GCLatencyMode.LowLatency
和GCLatencyMode.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
我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
最佳做法是在大多数情况下不强制进行垃圾收集. (我所研究的每个系统都强制进行垃圾收集,强调了问题,如果解决这个问题就会消除强制垃圾收集的需要,并大大加快了系统的速度.)
在某些情况下,您可以了解有关内存使用情况的更多信息.在多用户应用程序或一次响应多个请求的服务中,这不可能成立.
但是在某些批处理类型处理中,您确实了解GC.例如,考虑一个应用程序.
在命令行上给出了文件名列表
处理单个文件,然后将结果写入结果文件.
处理文件时,会创建大量互连对象,在文件处理完成之前无法收集这些对象(例如,解析树)
不保持它已处理的文件之间的匹配状态.
您可能能够(经过仔细测试)测试后应该在处理完每个文件后强制执行完整的垃圾回收.
另一种情况是每隔几分钟醒来处理一些物品,并且在睡着时不保持任何状态的服务.然后在睡觉之前强制完整收集可能是值得的.
我唯一一次考虑强制收集的是当我知道最近创建了很多对象时,当前引用的对象非常少.
我宁愿有一个垃圾收集API,当我可以给它提示这种类型的东西,而不必强迫我自己的GC.
另见" Rico Mariani的表演花絮 "
一种情况是当您尝试使用WeakReference对单元测试代码进行单元化时.
当你知道垃圾收集器没有的应用程序的性质时,你可以调用GC.Collect().很有可能认为,作为作者,这很有可能.然而,事实是GC相当于一个写得很好并且经过测试的专家系统,而且你很少知道它没有的低级代码路径.
我能想到你可能有一些额外信息的最佳例子是一个在空闲时段和非常繁忙的时段之间循环的应用程序.您希望在繁忙时段获得最佳性能,因此希望使用空闲时间进行一些清理.
但是,大多数情况下,GC无论如何都足够聪明.
在大型24/7或24/6系统中 - 对消息,RPC请求或连续轮询数据库或进程的系统作出反应的系统 - 有一种识别内存泄漏的方法很有用.为此,我倾向于为应用程序添加一种机制,以暂时挂起任何处理,然后执行完整的垃圾收集.这使系统处于静止状态,其中剩余的内存是合法的长期存储器(缓存,配置等)或者是"泄漏"(不期望或希望被植根但实际上是对象的对象).
使用此机制可以更轻松地分析内存使用情况,因为报告不会受到活动处理噪音的影响.
为了确保获得所有垃圾,您需要执行两个集合:
GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
由于第一个集合将导致具有终结器的任何对象被最终化(但实际上并不垃圾收集这些对象).第二个GC将垃圾收集这些已完成的对象.
作为内存碎片解决方案.在将大量数据写入内存流(从网络流中读取)时,我遇到了内存不足的问题.数据以8K块的形式写成.达到128M后,即使有大量可用内存(但它是碎片化的),也有异常.调用GC.Collect()解决了这个问题.修复后我能够处理超过1G.
看看Rico Mariani撰写的这篇文章.他在调用GC.Collect时给出两条规则(规则1是:"不要"):
何时调用GC.Collect()
通过Interop自动化Microsoft Office时,几乎需要调用GC.Collect()的一个实例。Office的COM对象不喜欢自动释放,并且可能导致Office产品实例占用大量内存。我不确定这是问题还是设计使然。互联网上有很多关于此主题的文章,因此我不会赘述。
使用Interop进行编程时,通常应通过使用Marshal.ReleseComObject()来手动释放每个 COM对象。另外,手动调用垃圾回收可以帮助“清理”。使用完Interop对象后,调用以下代码似乎会有所帮助:
GC.Collect() GC.WaitForPendingFinalizers() GC.Collect()
以我个人的经验,结合使用ReleaseComObject和手动调用垃圾回收会大大减少Office产品(特别是Excel)的内存使用量。
我正在对数组和列表进行一些性能测试:
private static int count = 100000000; private static ListGetSomeNumbers_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();