Практическое использование System.WeakReference - PullRequest
36 голосов
/ 19 августа 2008

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

Ответы [ 4 ]

46 голосов
/ 19 августа 2008

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

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

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

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

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

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

13 голосов
/ 19 августа 2008

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

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 голосов
/ 07 декабря 2008

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

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

0 голосов
/ 25 августа 2012

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

  1. Вместо глобальных объектов, объявленных как статические : Глобальные объекты объявляются как статические поля, и статические поля не могут быть обработаны GC (сборщик мусора) до тех пор, пока AppDomain не будет GC. Таким образом, вы рискуете исключения из памяти. Вместо этого мы можем обернуть глобальный объект в 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, нам понадобится метод с отложенной загрузкой, который я опущу для краткости.

    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-потоке.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...