好吧,这里有三件事......
首先,开始使用.NET的服务器GC:http: //msdn.microsoft.com/en-us/library/ms229357.aspx。这可能会使您的应用程序不被阻塞。
其次,如果你可以在你的虚拟机上做到这一点:检查更新。这似乎总是很明显,但我见过很多情况下简单的 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' 中的 IEnumerable 实际上是 IDisposable,因此实现了 IDisposable 模式。将你的“yield”实现与锁结合起来,忘记通过用“MoveNext”等枚举它来调用 IDisposable,你会得到一些反映上述情况的非常讨厌的行为。特别是因为终结器是由一个单独的线程(!)从终结队列中调用的。将它与无限循环或线程不安全代码结合起来,你会得到一些非常讨厌的意外行为,这些行为会在特殊情况下触发(当内存耗尽时,或者当 GC 事情它应该做的事情时)。
换句话说:我会检查你的一次性用品和终结器,并对它们非常挑剔。检查 'yield' 是否有隐式终结器,并确保从同一个线程调用 IDisposable。您要警惕的一些事情的例子:
try
{
for (int i = 0; i < 10; ++i)
{
yield return "foo";
}
}
finally
{
// Called by IDisposable
}
和
lock (myLock) // 'lock' and 'using' also trigger IDisposable
{
yield return "foo";
}