Pergunta

eu entendo o que System.WeakReference faz, mas o que não consigo entender é um exemplo prático de para que pode ser útil.A aula em si me parece, bem, um hack.Parece-me que existem outros meios melhores de resolver um problema em que um WeakReference é usado nos exemplos que vi.Qual é o exemplo canônico de onde você realmente precisa usar um WeakReference?Não estamos tentando conseguir mais longe desse tipo de comportamento e uso dessa classe?

Foi útil?

Solução

Um exemplo útil são os caras que executam o banco de dados orientado a objetos DB4O.Lá, WeakReferences são usados ​​como uma espécie de light cache:ele manterá seus objetos na memória apenas enquanto seu aplicativo o fizer, permitindo que você coloque um cache real no topo.

Outro uso seria na implementação de manipuladores de eventos fracos.Atualmente, uma grande fonte de vazamentos de memória em aplicativos .NET é o esquecimento de remover manipuladores de eventos.Por exemplo.

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

Veja o problema?No trecho acima, MyForm será mantido ativo na memória para sempre enquanto MyApplication estiver ativo na memória.Crie 10 MyForms, feche todos, seus 10 MyForms ainda estarão na memória, mantidos ativos pelo manipulador de eventos.

Insira Referência Fraca.Você pode construir um manipulador de eventos fraco usando WeakReferences para que someHandler seja um manipulador de eventos fraco para MyApplication.Foo, corrigindo assim seus vazamentos de memória!

Isto não é apenas teoria.Dustin Campbell do blog DidItWith.NET postou uma implementação de manipuladores de eventos fracos usando System.WeakReference.

Outras dicas

Eu o uso para implementar um cache onde as entradas não utilizadas são automaticamente coletadas como lixo:

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

    /* ... */
   }

Eu uso referência fraca para manutenção de estado em mixins.Lembre-se, mixins são estáticos, então quando você usa um objeto estático para anexar estado a um não estático, você nunca sabe quanto tempo será necessário.Então, em vez de manter um Dictionary<myobject, myvalue> Eu mantenho um Dictionary<WeakReference,myvalue> para evitar que o mixin arraste as coisas por muito tempo.

O único problema é que toda vez que faço um acesso, também verifico se há referências mortas e as removo.Não que machuquem alguém, a menos que sejam milhares, é claro.

Existem duas razões pelas quais você usaria WeakReference.

  1. Em vez de objetos globais declarados como estáticos:Objetos globais são declarados como campos estáticos e os campos estáticos não podem ser GC'ed (coletados como lixo) até que o AppDomain é GC'ed.Então você corre o risco de exceções de falta de memória.Em vez disso, podemos envolver o objeto global em um WeakReference.Mesmo que a WeakReference em si for declarado estático, o objeto para o qual ele aponta será submetido a GC quando a memória estiver baixa.

    Basicamente, use wrStaticObject em vez de staticObject.

    class ThingsWrapper {
        //private static object staticObject = new object();
        private static WeakReference wrStaticObject 
            = new WeakReference(new object());
    }
    

    Aplicativo simples para provar que o objeto estático é coletado como lixo quando 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); }
    }
    

    Nota da saída abaixo staticThing é GC'ed no final, mesmo depois ThingsWrapper é - ou seja,GC'ed quando AppDomain é GC'ed.

    Thing() staticThing
    Thing() privateThing
    ~Thing() privateThing
    ~ThingsWrapper
    ~Thing() staticThing
    

    Em vez disso, podemos embrulhar Thing em um WeakReference.Como wrStaticThing pode ser GC, precisaremos de um método de carregamento lento que deixei de fora por questões de brevidade.

    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
    }
    

    Observe a saída abaixo que wrStaticThing é GC'ed quando o thread GC é invocado.

    Thing() wrStaticThing
    ~Thing() wrStaticThing
    ~WeakReferenceThing
    WeakReference is dead.
    
  2. Para objetos que demoram para inicializar:Você não deseja que objetos que consomem muito tempo para serem iniciados sejam GC.Você pode manter uma referência estática para evitar isso (com os contras do ponto acima) ou usar WeakReference.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top