Подтвердить что ты не робот

Практическое использование System.WeakReference

Я понимаю, что System.WeakReference делает, но то, что я не могу понять, является практическим примером того, что это может быть полезно для, Сам класс, похоже, мне нравится. Мне кажется, что есть другие, более эффективные способы решения проблемы, где WeakReference используется в примерах, которые я видел. Каков канонический пример того, где вы действительно должны использовать WeakReference? Разве мы не пытаемся найти дальше от этого типа поведения и использования этого класса?

4b9b3361

Ответ 1

Одним из полезных примеров являются парни, которые запускают объектно-ориентированную базу данных DB4O. Там WeakReferences используются как своего рода световой кеш: он будет хранить ваши объекты в памяти только до тех пор, пока ваше приложение будет работать, что позволит вам поставить реальный кеш сверху.

Еще одно применение было бы в реализации слабых обработчиков событий. В настоящее время один большой источник утечек памяти в .NET-приложениях забывает удалить обработчики событий. Например.

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

Увидеть проблему? В приведенном выше фрагменте MyForm будет храниться в памяти навсегда, пока MyApplication жив в памяти. Создайте 10 MyForms, закройте их все, ваши 10 MyForms по-прежнему будут в памяти, сохранены в памяти обработчиком событий.

Введите WeakReference. Вы можете создать слабый обработчик событий, используя WeakReferences, чтобы someHandler был слабым обработчиком событий для MyApplication.Foo, тем самым устраняя утечки памяти!

Это не просто теория. Дастин Кэмпбелл из блога DidItWith.NET опубликовал реализацию слабых обработчиков событий с использованием System.WeakReference.

Ответ 2

Я использую его для реализации кеша, в котором неиспользуемые записи автоматически собираются с мусором:

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

    /* ... */
   }

Ответ 3

Я использую слабую ссылку для сохранения состояния в mixins. Помните, mixins являются статическими, поэтому, когда вы используете статический объект для присоединения состояния к нестационарному, вы никогда не знаете, сколько времени потребуется. Поэтому вместо сохранения Dictionary<myobject, myvalue> я сохраняю Dictionary<WeakReference,myvalue>, чтобы предотвратить слишком медленное перемещение mixin.

Единственная проблема в том, что каждый раз, когда я делаю доступ, я также проверяю мертвые ссылки и удаляю их. Не то чтобы они причиняли кому-либо боль, если, конечно, не было тысяч.

Ответ 4

Есть две причины, по которым вы использовали бы WeakReference.

  • Вместо глобальных объектов, объявленных как статические: глобальные объекты объявляются как статические поля, а статические поля не могут быть GC'ed (сборщик мусора), пока AppDomain не будет GC'ed, Таким образом, вы рискуете исключениями вне памяти. Вместо этого мы можем обернуть глобальный объект в WeakReference. Даже если сам WeakReference объявлен статическим, объект, на который он указывает, будет GC'ed, когда память будет низкой.

    В основном используйте 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 является GC'ed в самом конце даже после того, как ThingsWrapper есть - то есть GC'ed, когда AppDomain является GC'ed.

    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
    }
    

    Обратите внимание, что на выходе ниже, что wrStaticThing является GC'ed при вызове потока GC.

    Thing() wrStaticThing
    ~Thing() wrStaticThing
    ~WeakReferenceThing
    WeakReference is dead.
    
  • Для объектов, требующих много времени для инициализации. Вы не хотите, чтобы объекты, которые являются время-consusming для init, должны быть GC'ed. Вы можете либо сохранить статическую ссылку, чтобы избежать этого (с минусами из вышеприведенной точки), либо использовать WeakReference.