考虑以下情况:
GC.Collect(GC.MaxGeneration);
GC.WaitForPendingFinalizers();
GC.Collect(GC.MaxGeneration);
考虑到多线程和垃圾回收模式,什么情况下会在WaitForPendingFinalizers
上出现死锁?
注意:我不希望得到有关为什么不应调用GC.Collect
的答案。
考虑以下情况:
GC.Collect(GC.MaxGeneration);
GC.WaitForPendingFinalizers();
GC.Collect(GC.MaxGeneration);
考虑到多线程和垃圾回收模式,什么情况下会在WaitForPendingFinalizers
上出现死锁?
注意:我不希望得到有关为什么不应调用GC.Collect
的答案。
// causes a deadlock when built with release config and no debugger attached
// building in debug mode and/or attaching the debugger might keep
// badIdea alive for longer, in which case you won't see the deadlock
// unless you explicitly set badIdea to null after calling Monitor.Enter
var badIdea = new BadIdea();
Monitor.Enter(badIdea);
GC.Collect(GC.MaxGeneration);
GC.WaitForPendingFinalizers();
GC.Collect(GC.MaxGeneration);
// ...
public class BadIdea
{
~BadIdea()
{
lock (this)
{
// ...
}
}
}
GC.Collect
和GC.WaitForPendingFinalizers
的调用不会导致死锁,除非您在Finalize
方法中访问托管对象。调用其他公共作用域对象的方法可能会导致不可预测的结果,包括死锁。原因是您无法完全控制这些其他对象的锁定模式。当您的终结器方法尝试访问它时,任何人都可以锁定它。
此外,在终结器中明确锁定this
几乎肯定会导致死锁,正如LukeH的答案所示。您可以在此处阅读Jeffrey Richter的原始文章。
通常,您应该仅在终结器中释放非托管资源,这应该消除有关死锁的任何顾虑。
Bitmap
对象很昂贵,因此需要存储数百个16x16图标的程序可能会定义一个类,该类分配16x1024像素位图,每个位图可以容纳64个图标,然后返回每个对象,其中包含对位图分配器的引用以及它所“拥有”的部分的指示。当这样一个“位图块”对象被处理时,分配器可以使较大位图的该部分可供其他人使用。 - supercatJeffrey Richter提到了一个著名的关于WaitForPendingFinalizers的死锁问题。具体情况可以在这里查看:http://haacked.com/archive/2005/04/12/neverlockthis.aspx
class MyClass { private bool isDisposed = false;
~MyClass() { lock(this) { if(!isDisposed) { // ... } } } } ... MyClass instance = new MyClass();
Monitor.Enter(instance); instance = null;
GC.Collect(); GC.WaitForPendingFinalizers();
这是因为错误地使用了lock(this)导致的。无论如何,这种情况下WaitForPendingFinalizers被锁定了。