一般建议不要在代码中调用GC.Collect
,但有哪些特例情况呢?
我只能想到几种非常特殊的情况,在这些情况下强制进行垃圾回收可能是有意义的。
其中一个例子是服务会以间隔时间唤醒,执行一些任务,然后休眠很长时间。在这种情况下,强制进行垃圾回收可能是个好主意,以防止即将变得空闲的进程占用比需要更多的内存。
除了这种情况之外,还有什么其他情况可以接受调用GC.Collect
吗?
一般建议不要在代码中调用GC.Collect
,但有哪些特例情况呢?
我只能想到几种非常特殊的情况,在这些情况下强制进行垃圾回收可能是有意义的。
其中一个例子是服务会以间隔时间唤醒,执行一些任务,然后休眠很长时间。在这种情况下,强制进行垃圾回收可能是个好主意,以防止即将变得空闲的进程占用比需要更多的内存。
除了这种情况之外,还有什么其他情况可以接受调用GC.Collect
吗?
GC.Collect
就相当于告诉垃圾回收器你比它更懂得如何清理内存。你为什么不喜欢这个例子呢? - Jon SkeetGC.Collect()
会请求GC执行完整的垃圾回收。如果您知道您刚刚使许多以前长期存在的对象有资格进行垃圾回收,并且您认为用户现在比以后更不可能注意到轻微的暂停,那么现在似乎是促使回收的更好时机,而不是让它以后发生。 - Jon Skeet我只在编写简单的性能/分析测试时使用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()
执行时状态尽可能相似——即 TestA()
接近临界点时,TestB()
不会受到太多影响。
一个经典的例子是一个简单的控制台应用程序(例如可以在这里发布的 Main
方法),它展示了循环字符串连接和 StringBuilder
之间的差异。
如果我需要精确的结果,那么这将是两个完全独立的测试——但通常只需最小化(或标准化)测试期间的垃圾回收以获得行为的大致感觉。
在生产代码中? 我还没有使用它 ;-p
我更希望有一个垃圾回收API,可以在不强制GC的情况下提供关于这种情况的提示。我唯一会考虑强制回收的情况是当我知道最近创建了大量对象并且当前只引用了很少的对象时。
最近我认为使用短暂的工作进程来处理每批工作,并让操作系统进行资源回收会更好,与上述情况类似。
在大型 24/7 或 24/6 系统中——响应消息、RPC 请求或连续轮询数据库或进程的系统——有一种识别内存泄漏的方法很有用。为此,我倾向于在应用程序中添加机制,将任何处理暂时挂起,然后执行完整的垃圾回收。这将使系统进入静止状态,在该状态下,剩余的内存要么是合法的长期内存(缓存、配置等),要么是“泄漏”的(不希望或预期成为根对象但实际上确实是的对象)。
拥有此机制可大大简化性能分析过程,因为报告不会被活动处理中的噪音干扰。
为确保获取所有垃圾,需要执行两次垃圾回收:
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
由于第一个集合将导致任何具有终结器的对象被终止(但不会实际回收这些对象),因此第二个垃圾回收将回收这些已经终止的对象。
当你了解应用程序的性质,而垃圾回收器(GC)无法了解时,你可以调用 GC.Collect()
。
作为作者,经常会觉得这很可能或很正常。然而,事实上,GC 是一个编写和测试相当不错的专家系统,你很少会知道它没有关于低级代码路径的信息。
我能想到的最好的例子是在空闲期和非常繁忙的时期之间循环的应用程序。你希望在繁忙期间获得最佳性能,因此想利用闲置时间进行一些清理工作。
然而,大多数情况下,GC 已经足够智能,可以自动完成这项工作。
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
根据我的个人经验,使用ReleaseComObject和手动调用垃圾回收相结合可以极大地减少Office产品,尤其是Excel的内存使用。
请看Rico Mariani的这篇文章,他给出了两个关于何时调用GC.Collect的规则(第一个规则是:“不要”):
我正在对数组和列表进行一些性能测试:
private static int count = 100000000;
private static List<int> GetSomeNumbers_List_int()
{
var lstNumbers = new List<int>();
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<int>\" 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));
}
我在 GetSomeNumbers_Enumerable_Range 方法中遇到了 OutOfMemoryException
,唯一的解决方法是通过以下方式释放内存:
numbers = null;
GC.Collect();