首先,开始使用 .NET 的服务器 GC:http://msdn.microsoft.com/en-us/library/ms229357.aspx。这样可能会让您的应用程序非阻塞。
其次,如果您可以在 VM 上进行操作,请检查更新。这似乎总是显而易见的,但我见过很多情况,一个简单的 Windows 更新就可以解决奇怪的问题。
第三点,我想提出一个关于对象生命周期的观点,这可能是其中一个问题。这个故事相当长,所以请耐心听我说。
对象的生命周期基本上是构建-垃圾回收-终结。所有三个过程都在单独的线程中运行。GC 将数据传递给终结线程,后者有一个队列来调用“析构函数”。
那么,如果您有一个做一些奇怪事情的终结器,比如:
public class FinalizerObject
{
public FinalizerObject(int n)
{
Console.WriteLine("Constructed {0}", n);
this.n = n;
}
private int n;
~FinalizerObject()
{
while (true) { Console.WriteLine("Finalizing {0}...", n); System.Threading.Thread.Sleep(1000); }
}
}
由于终结器在处理队列的单独线程中运行,因此拥有一个执行愚蠢操作的单个终结器对您的应用程序是一个严重的问题。您可以通过使用上述类2次来查看此问题:
static void Main(string[] args)
{
SomeMethod();
GC.Collect(GC.MaxGeneration);
GC.WaitForFullGCComplete();
Console.WriteLine("All done.");
Console.ReadLine();
}
static void SomeMethod()
{
var obj2 = new FinalizerObject(1);
var obj3 = new FinalizerObject(2);
}
请注意,如果您删除Thread.Sleep,您将遇到一个小的内存泄漏,如果不删除,则会出现100%的CPU进程 - 即使您的主线程仍在响应。因为它们是不同的线程,所以从这里开始很容易阻止整个进程 - 例如使用锁定:
static void Main(string[] args)
{
SomeMethod();
GC.Collect(GC.MaxGeneration);
GC.WaitForFullGCComplete();
Thread.Sleep(1000);
lock (lockObject)
{
Console.WriteLine("All done.");
}
Console.ReadLine();
}
static object lockObject = new Program();
static void SomeMethod()
{
var obj2 = new FinalizerObject(1, lockObject);
var obj3 = new FinalizerObject(2, lockObject);
}
[...]
~FinalizerObject()
{
lock (lockObject) { while (true) { Console.WriteLine("Finalizing {0}...", n); System.Threading.Thread.Sleep(1000); } }
}
所以我可以看到你在想“你是认真的吗?”;事实上,你可能正在做这样的事情,甚至没有意识到。这就是“yield”的作用:
从“yield”返回的IEnumerable实际上是IDisposable,并且作为实现IDisposable模式。将您的“yield”实现与锁定组合,忘记使用'MoveNext'枚举调用IDisposable等,您将得到一些相当令人讨厌的行为,反映了上述情况。特别是因为终结器是由单独的线程从终结队列中调用的!将其与无限循环或线程不安全的代码结合使用,您将获得一些相当令人讨厌的意外行为,在异常情况下触发(当内存耗尽或GC认为它应该执行某些操作时)。
换句话说:我会检查您的可处置物和终结器,并对它们非常挑剔。检查“yield”是否具有隐式终结器,并确保您从同一线程调用IDisposable。以下是一些需要注意的事项的示例:
try
{
for (int i = 0; i < 10; ++i)
{
yield return "foo";
}
}
finally
{
}
和
lock (myLock) // 'lock' and 'using' also trigger IDisposable
{
yield return "foo";
}