Tengo una clase que se aferra a un delegado, con el fin de evaluar perezosamente algo más tarde.GC de delegados, ¿qué me estoy perdiendo? (mi delegado no se recoge)
Una vez que lo he evaluado, llamando al delegado, borro la referencia al delegado, esperando que sea elegible para la recopilación. Después de todo, podría aferrarse a un mundo de variables locales si se construyera como un método anónimo.
Traté de construir una prueba unitaria para verificar esto, pero parece que no funciona de la manera que lo planeé, en cambio parece que mis suposiciones sobre WeakReference
(que utilicé para fines de prueba aquí), o alguna otra suposición, no tiene agua.
Tome un vistazo a este código, que se puede ejecutar en LINQPad
void Main()
{
WeakReference wr;
Lazy<int> l;
CreateTestData(out wr, out l);
wr.IsAlive.Dump(); // should be alive here
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
wr.IsAlive.Dump(); // and alive here as well
l.Value.Dump(); // but now we clear the reference
GC.Collect(); // so one of these should collect it
GC.WaitForPendingFinalizers();
GC.Collect();
wr.IsAlive.Dump(); // and then it should be gone here
GC.KeepAlive(l);
}
void CreateTestData(out WeakReference wr, out Lazy<int> l)
{
Func<int> f =() => 10;
wr = new WeakReference(f);
l = new Lazy<int>(f);
}
public class Lazy<T>
{
private Func<T> _GetValue;
private T _Value;
public Lazy(Func<T> getValue)
{
_GetValue = getValue;
}
public T Value
{
get
{
if (_GetValue != null)
{
_Value = _GetValue();
_GetValue = null;
}
return _Value;
}
}
}
estaba asumiendo que:
- Independientemente de versión de depuración, depurador asociado, desde que cree el delegado en una método separado, del cual vuelvo, no debería haber nada aferrado al delegado, excepto el
WeakReference
y elLazy<T>
objetos - Si le pregunto al
Lazy<T>
objeto a renunciar a su referencia al delegado, lo que reduciría las referencias a sólo el queWeakReference
se aferra a - Y luego forzar una recolección completa, suponiendo que si la única izquierda referencia es el que está en
WeakReference
a continuación, el delegado
- serían recogidos, y mi
WeakReference
indicaría que el objeto ya no está vivo
la salida del código fue por lo tanto espera que sea (con comentarios):
true // not gc'ed after construction
true // not gc'ed after full GC, still beind held by Lazy<T>
10 // value from calling delegate
false // but is now gc'ed, Lazy<T> no longer has a reference to it
Pero en lugar de la salida es:
true
true
10
true
¿Alguien puede arrojar algo de luz sobre lo que me falta aquí?
Ok, eso tiene sentido. Cambié el delegado para que usara una variable local para calcular sus resultados, y eso fue efectivamente recopilado. –
Pregunta lateral: supongo que el compilador no es lo suficientemente inteligente como para darse cuenta de que múltiples delegados, del mismo tipo, que devuelven el mismo valor constante, pueden crearse una vez y reutilizarse en todas partes. –
Básicamente, mi objetivo era realizar una prueba que verifique que la referencia al delegado se abandona en el momento adecuado, para que sea posible para la recolección de basura donde esté permitido, no necesariamente que ese delegado en particular fue recolectado.Cambiar al delegado solucionó ligeramente ese problema, y ahora las pruebas se ejecutan como se esperaba. –