弱参考
在 .NET 中,当没有任何引用时,GC 会分配对象。因此,虽然仍然可以从代码到达对象(有强烈的引用),但 GC 不会分配此对象。如果有很多大型对象,这可能会成为一个问题。
弱引用是一个引用,它允许 GC 在仍允许访问对象的同时收集对象。弱引用仅在不存在强引用时收集对象之前的不确定时间内有效。当你使用弱引用时,应用程序仍然可以获得对该对象的强引用,从而阻止其被收集。因此,弱引用对于保留初始化成本高昂的大对象非常有用,但如果它们没有被主动使用,则应该可用于垃圾回收。
简单用法:
WeakReference reference = new WeakReference(new object(), false);
GC.Collect();
object target = reference.Target;
if (target != null)
DoSomething(target);
因此,弱引用可用于维护对象的缓存。但是,重要的是要记住,在重新建立强引用之前,垃圾收集器始终存在到达对象的风险。
弱引用也可以方便地避免内存泄漏。典型的用例是事件。
假设我们对源上的事件有一些处理程序:
Source.Event += new EventHandler(Handler)
此代码注册一个事件处理程序,并创建一个从事件源到侦听对象的强引用。如果源对象的生命周期比侦听器长,并且当没有其他引用时,侦听器不再需要该事件,则使用普通的 .NET 事件会导致内存泄漏:源对象将侦听器对象保存在内存中应该是垃圾收集。
在这种情况下,使用弱事件模式可能是个好主意。
就像是:
public static class WeakEventManager
{
public static void SetHandler<S, TArgs>(
Action<EventHandler<TArgs>> add,
Action<EventHandler<TArgs>> remove,
S subscriber,
Action<S, TArgs> action)
where TArgs : EventArgs
where S : class
{
var subscrWeakRef = new WeakReference(subscriber);
EventHandler<TArgs> handler = null;
handler = (s, e) =>
{
var subscrStrongRef = subscrWeakRef.Target as S;
if (subscrStrongRef != null)
{
action(subscrStrongRef, e);
}
else
{
remove(handler);
handler = null;
}
};
add(handler);
}
}
并像这样使用:
EventSource s = new EventSource();
Subscriber subscriber = new Subscriber();
WeakEventManager.SetHandler<Subscriber, SomeEventArgs>(a => s.Event += a, r => s.Event -= r, subscriber, (s,e) => { s.HandleEvent(e); });
在这种情况下,我们当然有一些限制 - 事件必须是 a
public event EventHandler<SomeEventArgs> Event;
正如 MSDN 建议的那样:
- 仅在必要时使用长弱引用,因为在完成后对象的状态是不可预测的。
- 避免对小对象使用弱引用,因为指针本身可能大或大。
- 避免使用弱引用作为内存管理问题的自动解决方案。相反,开发一个有效的缓存策略来处理应用程序的对象。