【发布时间】:2012-12-22 08:55:08
【问题描述】:
前言:我知道如何解决问题。我想知道为什么会出现。请从上到下阅读问题。
众所周知,添加事件处理程序会导致 C# 中的内存泄漏。见Why and How to avoid Event Handler memory leaks?
另一方面,对象通常具有相似或相互关联的生命周期,因此没有必要取消注册事件处理程序。考虑这个例子:
using System;
public class A
{
private readonly B b;
public A(B b)
{
this.b = b;
b.BEvent += b_BEvent;
}
private void b_BEvent(object sender, EventArgs e)
{
// NoOp
}
public event EventHandler AEvent;
}
public class B
{
private readonly A a;
public B()
{
a = new A(this);
a.AEvent += a_AEvent;
}
private void a_AEvent(object sender, EventArgs e)
{
// NoOp
}
public event EventHandler BEvent;
}
internal class Program
{
private static void Main(string[] args)
{
B b = new B();
WeakReference weakReference = new WeakReference(b);
b = null;
GC.Collect();
GC.WaitForPendingFinalizers();
bool stillAlive = weakReference.IsAlive; // == false
}
}
A 和B 通过事件隐式相互引用,但 GC 可以删除它们(因为它不是使用引用计数,而是标记和清除)。
但现在考虑这个类似的例子:
using System;
using System.Timers;
public class C
{
private readonly Timer timer;
public C()
{
timer = new Timer(1000);
timer.Elapsed += timer_Elapsed;
timer.Start(); // (*)
}
private void timer_Elapsed(object sender, ElapsedEventArgs e)
{
// NoOp
}
}
internal class Program
{
private static void Main(string[] args)
{
C c = new C();
WeakReference weakReference = new WeakReference(c);
c = null;
GC.Collect();
GC.WaitForPendingFinalizers();
bool stillAlive = weakReference.IsAlive; // == true !
}
}
为什么GC不能删除C对象?为什么 Timer 使对象保持活动状态?计时器是否通过计时器机制的某些“隐藏”引用(例如静态引用)保持活动状态?
(*) 注意:如果只创建了计时器,没有启动,则不会出现问题。如果它已启动然后又停止,但事件处理程序未取消注册,则问题仍然存在。
【问题讨论】:
-
我也遇到了这个问题,我的解决方案是在不再需要计时器时停止计时器,然后您的 C 类将被垃圾收集。
-
我真的只是停止阅读另一种语言的主题,其中计时器在 iPad 上保持对象处于活动状态,他们只是确保在完成计时器并释放对象后处理掉它也可以丢弃。
-
值得注意的是,它与
C类对定时器的引用无关,尝试删除该字段,只使用一个局部变量作为定时器。