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

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


person Peter Meyer    schedule 19.08.2008    source источник


Ответы (4)


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

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

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

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

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

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

person Judah Gabriel Himango    schedule 19.08.2008
comment
Да, это довольно здорово. Мы приняли его код здесь на работе с некоторыми дополнениями для обработки других типов обработчиков событий (например, неуниверсальный обработчик событий, PropertyChangedEventHandler и т. Д.). У нас это получилось очень хорошо. - person Judah Gabriel Himango; 25.03.2009
comment
Ссылка, предоставленная для реализации слабых обработчиков событий, кажется, теперь мертва. у кого-нибудь есть альтернативные ссылки порекомендовать? - person Pissu Pusa; 29.01.2020
comment
Вот пример: github.com/adbancroft/WeakEvents. Фоди / blob / master / - person Judah Gabriel Himango; 30.01.2020

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

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

    /* ... */
   }
person Mark Cidade    schedule 19.08.2008

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

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

person Dmitri Nesteruk    schedule 07.12.2008
comment
Начиная с .NET 4.0, ConditionalWeakTable вероятно, более подходит для этого использования. - person user2864740; 02.11.2020
comment
В частности, CWT автоматически закрывает последнее примечание «[не забудьте] проверить мертвые ссылки и удалить их», так как Dictionary ‹WeakRef, Value› не позволяет восстанавливать значения до тех пор, пока записи фактически не будут удаленный. - person user2864740; 02.11.2020

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

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

    Обычно используйте 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 is - т.е. 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 вызывается сборщиком мусора при вызове потока сборщика мусора.

    Thing() wrStaticThing
    ~Thing() wrStaticThing
    ~WeakReferenceThing
    WeakReference is dead.
    
  2. Для объектов, инициализация которых требует много времени: вы не хотите, чтобы объекты, инициализация которых требует много времени, подвергались сборке мусора. Вы можете сохранить статическую ссылку, чтобы избежать этого (с минусами из вышеупомянутого пункта), или использовать WeakReference.

person hIpPy    schedule 25.08.2012