System.WeakReference的实际使用

时间:2008-08-19 02:45:28

标签: .net garbage-collection

我理解System.WeakReference的作用,但我似乎无法理解的是它可能有用的实际例子。在我看来,班级本身就是一个黑客。在我看来,还有其他更好的解决问题的方法,在我看到的例子中使用了WeakReference。你真正需要使用WeakReference的典型例子是什么?我们是不是试图让更远远离这种类型的行为并使用这个类?

4 个答案:

答案 0 :(得分:46)

一个有用的例子是运行DB4O面向对象数据库的人。在那里,WeakReferences被用作一种轻量级缓存:它只会在您的应用程序执行时将对象保留在内存中,从而允许您将真正的缓存放在最顶层。

另一个用途是在弱事件处理程序的实现中。目前,.NET应用程序中的一个重要内存泄漏源是忘记删除事件处理程序。 E.g。

public MyForm()
{
    MyApplication.Foo += someHandler;
}

看到问题?在上面的代码片段中,只要MyApplication在内存中存活,MyForm将永远保存在内存中。创建10个MyForms,关闭它们,你的10个MyForms仍将在内存中,由事件处理程序保持活着。

输入WeakReference。您可以使用WeakReferences构建一个弱事件处理程序,以便someHandler是MyApplication.Foo的弱事件处理程序,从而修复内存泄漏!

这不仅仅是理论。来自DidItWith.NET博客的Dustin Campbell使用System.WeakReference发布了an implementation of weak event handlers

答案 1 :(得分:13)

我用它来实现一个缓存,其中未使用的条目被自动垃圾收集:

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;
     }

    /* ... */
   }

答案 2 :(得分:2)

我在mixins中使用弱引用进行状态保持。请记住,mixins是静态的,因此当您使用静态对象将状态附加到非静态对象时,您永远不知道它需要多长时间。因此,我保留Dictionary<myobject, myvalue>而不是保留Dictionary<WeakReference,myvalue>,以防止混合物拖延太长时间。

唯一的问题是,每次我进行访问时,我都会检查死引用并删除它们。并不是说他们伤害了任何人,当然,除非有成千上万的人。

答案 3 :(得分:0)

使用WeakReference有两个原因。

  1. 而不是声明为静态的全局对象:全局对象被声明为静态字段,静态字段无法进行GC(垃圾收集),直到AppDomain为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输出中的注意事项即使在AppDomain之后也是GC,即Thing() staticThing Thing() privateThing ~Thing() privateThing ~ThingsWrapper ~Thing() staticThing 为GC时的GC。

    Thing

    相反,我们可以将WeakReference包裹在wrStaticThing中。由于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
  2. 对于初始化非常耗时的对象:您不希望对init进行时间考虑的对象是GC。您可以保留静态引用以避免这种情况(使用上面的缺点)或使用{{1}}。