【发布时间】:2008-08-19 02:45:27
【问题描述】:
我了解System.WeakReference 的作用,但我似乎无法理解它可能有用的实际示例。在我看来,这门课本身就是一个 hack。在我看来,还有其他更好的方法可以解决在我见过的示例中使用 WeakReference 的问题。什么是您真正必须使用 WeakReference 的典型示例?我们不是试图让这种行为和使用这个类更远吗?
【问题讨论】:
我了解System.WeakReference 的作用,但我似乎无法理解它可能有用的实际示例。在我看来,这门课本身就是一个 hack。在我看来,还有其他更好的方法可以解决在我见过的示例中使用 WeakReference 的问题。什么是您真正必须使用 WeakReference 的典型示例?我们不是试图让这种行为和使用这个类更远吗?
【问题讨论】:
一个有用的例子是运行 DB4O 面向对象数据库的人。在那里,WeakReferences 被用作一种轻型缓存:它只会将您的对象保留在内存中,只要您的应用程序这样做,您就可以在顶部放置一个真正的缓存。
另一个用途是实现弱事件处理程序。目前,.NET 应用程序中内存泄漏的一大来源是忘记删除事件处理程序。例如
public MyForm()
{
MyApplication.Foo += someHandler;
}
看到问题了吗?在上面的 sn-p 中,只要 MyApplication 在内存中存在,MyForm 就会在内存中永远保持活跃。创建 10 个 MyForms,将它们全部关闭,您的 10 个 MyForms 仍将保留在内存中,由事件处理程序保持活动状态。
输入弱引用。您可以使用 WeakReferences 构建弱事件处理程序,以便 someHandler 成为 MyApplication.Foo 的弱事件处理程序,从而修复您的内存泄漏!
这不仅仅是理论。 DidItWith.NET 博客的 Dustin Campbell 使用 System.WeakReference 发布了an implementation of weak event handlers。
【讨论】:
我用它来实现一个缓存,其中未使用的条目会自动被垃圾收集:
class Cache<TKey,TValue> : IEnumerable<KeyValuePair<TKey,TValue>>
{ Dictionary<TKey,WeakReference> dict = new Dictionary<TKey,WeakReference>();
public TValue this[TKey key]
{ get {lock(dict){ return getInternal(key);}}
set {lock(dict){ setInteral(key,value);}}
}
void setInteral(TKey key, TValue val)
{ if (dict.ContainsKey(key)) dict[key].Target = val;
else dict.Add(key,new WeakReference(val));
}
public void Clear() { dict.Clear(); }
/// <summary>Removes any dead weak references</summary>
/// <returns>The number of cleaned-up weak references</returns>
public int CleanUp()
{ List<TKey> toRemove = new List<TKey>(dict.Count);
foreach(KeyValuePair<TKey,WeakReference> kv in dict)
{ if (!kv.Value.IsAlive) toRemove.Add(kv.Key);
}
foreach (TKey k in toRemove) dict.Remove(k);
return toRemove.Count;
}
public bool Contains(string key)
{ lock (dict) { return containsInternal(key); }
}
bool containsInternal(TKey key)
{ return (dict.ContainsKey(key) && dict[key].IsAlive);
}
public bool Exists(Predicate<TValue> match)
{ if (match==null) throw new ArgumentNullException("match");
lock (dict)
{ foreach (WeakReference weakref in dict.Values)
{ if ( weakref.IsAlive
&& match((TValue) weakref.Target)) return true;
}
}
return false;
}
/* ... */
}
【讨论】:
我在 mixins 中使用弱引用来保持状态。请记住,mixin 是静态的,因此当您使用静态对象将状态附加到非静态对象时,您永远不知道需要多长时间。所以我没有保留Dictionary<myobject, myvalue>,而是保留了Dictionary<WeakReference,myvalue>,以防止mixin拖得太久。
唯一的问题是,每次访问时,我都会检查死引用并删除它们。并不是说他们伤害了任何人,除非有成千上万,当然。
【讨论】:
您使用WeakReference 的原因有两个。
而不是声明为静态的全局对象:全局对象被声明为静态字段,并且静态字段在AppDomain 被 GC 之前不能被 GC(垃圾收集)。所以你冒着内存不足异常的风险。相反,我们可以将全局对象包装在 WeakReference 中。即使WeakReference 本身被声明为静态的,它指向的对象也会在内存不足时被 GC 处理。
基本上,使用wrStaticObject 而不是staticObject。
class ThingsWrapper {
//private static object staticObject = new object();
private static WeakReference wrStaticObject
= new WeakReference(new object());
}
简单的应用程序来证明当 AppDomain 被垃圾回收时静态对象被垃圾回收。
class StaticGarbageTest
{
public static void Main1()
{
var s = new ThingsWrapper();
s = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
class ThingsWrapper
{
private static Thing staticThing = new Thing("staticThing");
private Thing privateThing = new Thing("privateThing");
~ThingsWrapper()
{ Console.WriteLine("~ThingsWrapper"); }
}
class Thing
{
protected string name;
public Thing(string name) {
this.name = name;
Console.WriteLine("Thing() " + name);
}
public override string ToString() { return name; }
~Thing() { Console.WriteLine("~Thing() " + name); }
}
注意staticThing 下面的输出即使在 ThingsWrapper 被 GC 之后也是在最后被 GC - 即当 AppDomain 被 GC 时被 GC。
Thing() staticThing
Thing() privateThing
~Thing() privateThing
~ThingsWrapper
~Thing() staticThing
相反,我们可以将Thing 包装在WeakReference 中。由于wrStaticThing 可以被 GC'ed,我们需要一个延迟加载的方法,为了简洁起见,我省略了它。
class WeakReferenceTest
{
public static void Main1()
{
var s = new WeakReferenceThing();
s = null;
GC.Collect();
GC.WaitForPendingFinalizers();
if (WeakReferenceThing.wrStaticThing.IsAlive)
Console.WriteLine("WeakReference: {0}",
(Thing)WeakReferenceThing.wrStaticThing.Target);
else
Console.WriteLine("WeakReference is dead.");
}
}
class WeakReferenceThing
{
public static WeakReference wrStaticThing;
static WeakReferenceThing()
{ wrStaticThing = new WeakReference(new Thing("wrStaticThing")); }
~WeakReferenceThing()
{ Console.WriteLine("~WeakReferenceThing"); }
//lazy-loaded method to new Thing
}
请注意,在调用 GC 线程时,wrStaticThing 会被 GC 处理。
Thing() wrStaticThing
~Thing() wrStaticThing
~WeakReferenceThing
WeakReference is dead.
对于初始化耗时的对象:您不希望对初始化耗时的对象进行 GC。您可以保留一个静态引用来避免这种情况(从上述观点来看有缺点)或使用WeakReference。
【讨论】: