Personalmente no me he encontrado con una situación en la que haya necesitado usar el tipo WeakReference en .Net, pero la creencia popular parece ser que debería usarse en cachés. El Dr. Jon Harrop dio un muy buen caso contra el uso de WeakReferences en cachés en su respuesta a esta pregunta.
También he escuchado a menudo que los desarrolladores de AS3 hablan sobre el uso de referencias débiles para ahorrar en la huella de la memoria, pero según las conversaciones que he tenido, parece agregar complejidad sin lograr necesariamente el objetivo previsto, y el comportamiento en tiempo de ejecución es bastante impredecible. Tanto es así que muchos simplemente se dan por vencidos y, en cambio, administran el uso de la memoria con más cuidado / optimizan su código para que consuman menos memoria (o para compensar más ciclos de CPU y menor huella de memoria).
El Dr. Jon Harrop también señaló en su respuesta que las referencias débiles .Net no son suaves, y hay una colección agresiva de referencias débiles en gen0. Según MSDN , ¡las referencias largas y débiles le brindan el potencial de recrear un objeto but the state of the object remains unpredictable.
!
Dadas estas características, no puedo pensar en una situación en la que las referencias débiles serían útiles, ¿tal vez alguien podría iluminarme?
fuente
Respuestas:
He encontrado aplicaciones prácticas legítimas de referencias débiles en los siguientes tres escenarios del mundo real que realmente me sucedieron personalmente:
Aplicación 1: controladores de eventos
Eres emprendedor Su empresa vende un control de líneas de chispa para WPF. Las ventas son excelentes, pero los costos de soporte lo están matando. Demasiados clientes se quejan de la acumulación de CPU y las pérdidas de memoria cuando se desplazan a través de pantallas llenas de líneas de chispa. El problema es que su aplicación está creando nuevas líneas de chispa a medida que aparecen, pero el enlace de datos está evitando que las viejas se recojan. ¿Qué haces?
Introduzca una referencia débil entre el enlace de datos y su control para que el enlace de datos solo ya no evite que su control se recolecte basura. Luego, agregue un finalizador a su control que elimine el enlace de datos cuando se recopile.
Aplicación 2: gráficos mutables
Eres el próximo John Carmack. Has inventado una nueva representación ingeniosa basada en gráficos de superficies de subdivisión jerárquica que hace que los juegos de Tim Sweeney parezcan una Nintendo Wii. Obviamente, no voy a decir exactamente cómo funciona, pero todo se centra en este gráfico mutable donde los vecinos de un vértice se pueden encontrar en a
Dictionary<Vertex, SortedSet<Vertex>>
. La topología del gráfico sigue cambiando a medida que el jugador corre. Solo hay un problema: su estructura de datos está eliminando subgráficos inalcanzables a medida que se ejecuta y necesita eliminarlos o perderá memoria. Afortunadamente, eres un genio, así que sabes que hay una clase de algoritmos diseñados específicamente para localizar y recolectar subgrafías inalcanzables: ¡recolectores de basura! Uno lee excelente monografía Richard Jones' sobre el temapero te deja perplejo y preocupado por tu inminente plazo. ¿Qué haces?¡Simplemente reemplazando su
Dictionary
con una tabla de hash débil, puede aprovechar el GC existente y hacer que recopile automáticamente sus subgrafos inalcanzables para usted! Volver a hojear los anuncios de Ferrari.Aplicación 3: Decorar árboles
Estás colgado del techo de una habitación cilíndrica con un teclado. Tienes 60 segundos para examinar BIG DATA antes de que alguien te encuentre. Llegaste preparado con un hermoso analizador basado en secuencias que se basa en el GC para recolectar fragmentos de AST después de haber sido analizados. Pero se da cuenta de que necesita metadatos adicionales en cada AST
Node
y que los necesita rápidamente. ¿Qué haces?Puede usar a
Dictionary<Node, Metadata>
para asociar metadatos con cada nodo, pero, a menos que lo borre, las fuertes referencias del diccionario a los nodos AST antiguos los mantendrán vivos y perderán memoria. La solución es una tabla hash débil que mantiene solo referencias débiles a las claves y la basura recolecta enlaces de valores clave cuando la clave se vuelve inalcanzable. Luego, a medida que los nodos AST se vuelven inalcanzables, se recogen basura y su enlace clave-valor se elimina del diccionario, dejando los metadatos correspondientes inalcanzables para que también se recopilen. Luego, todo lo que tiene que hacer después de que su circuito principal haya terminado es deslizarse hacia arriba a través del respiradero recordando reemplazarlo justo cuando entra el guardia de seguridad.Tenga en cuenta que en las tres aplicaciones del mundo real que realmente me sucedieron, quería que el GC se recopilara de la forma más agresiva posible. Es por eso que estas son aplicaciones legítimas. Todos los demás están equivocados.
fuente
ConditionalWeakTable
en .NET).ConditionalWeakTable
es lo que usarían las aplicaciones 2 y 3, mientras que algunas personas en otras publicaciones realmente usanDictionary<WeakReference, T>
. No tengo idea de por qué: siempre terminarías con una tonelada deWeakReference
valores nulos con valores a los que ninguna tecla puede acceder independientemente de cómo lo hagas. RidikDocumento de Microsoft Patrones de eventos débiles .
fuente
Déjame poner esto primero y volver a ello:
Entonces, comencemos desde el principio:
- Disculpas de antemano por cualquier ofensa no intencional, pero voy a retroceder al nivel de "Dick y Jane" por un momento ya que uno nunca puede decirle a la audiencia.
Entonces, cuando tienes un objeto
X
, especificémoslo como una instancia declass Foo
, NO PUEDE vivir solo (principalmente cierto); De la misma manera que "Ningún hombre es una isla", solo hay unas pocas formas en que un objeto puede promoverse a Islandhood, aunque se llama ser una raíz GC en CLR. Ser una Raíz GC, o tener una cadena establecida de conexiones / referencias a una raíz GC, es básicamente lo que determina siFoo x = new Foo()
se recolecta o no la basura.Si no puede caminar de regreso a alguna raíz del GC, ya sea caminando por el montón o apilando, queda efectivamente huérfano y probablemente será marcado / recogido el próximo ciclo.
En este punto, veamos algunos ejemplos horriblemente inventados:
Primero, nuestro
Foo
:Bastante simple: no es seguro para subprocesos, así que no intente eso, pero mantiene un "recuento de referencias" aproximado de instancias activas y decrementos cuando se finalizan.
Ahora echemos un vistazo a
FooConsumer
:Entonces, tenemos un objeto que ya es una raíz de GC propia (bueno ... para ser específicos, se enraizará a través de una cadena directamente al dominio de la aplicación que ejecuta esta aplicación, pero ese es otro tema) que tiene dos métodos de engancharse a una
Foo
instancia - probémoslo:Ahora, a partir de lo anterior, ¿esperaría que el objeto al que se hizo referencia alguna vez
f
fuera "coleccionable"?No, porque ahora hay otro objeto que tiene una referencia a él, el
Dictionary
de esaSingleton
instancia estática.Ok, intentemos con el enfoque débil:
Ahora, cuando criticamos nuestra referencia a lo
Foo
que fue una vezf
, no hay más referencias "duras" al objeto, por lo que es coleccionable: loWeakReference
creado por el oyente débil no lo impedirá.Buenos casos de uso:
Controladores de eventos (aunque lea esto primero: Eventos débiles en C # )
Tienes una situación en la que causarías una "referencia recursiva" (es decir, el objeto A se refiere al objeto B, que se refiere al objeto A, también conocido como "Fuga de memoria")(editar: derp, por supuesto, esto no es no es cierto)Desea "transmitir" algo a una colección de objetos, pero no quiere ser lo que los mantenga vivos; a
List<WeakReference>
puede mantenerse fácilmente e incluso podarse quitando donderef.Target == null
fuente
Al igual que las filtraciones lógicas que son realmente difíciles de rastrear, mientras que los usuarios tienden a notar que ejecutar su software durante mucho tiempo tiende a ocupar más y más memoria y se vuelve más y más lento hasta que se reinicia. Yo no.
Tenga en cuenta lo que sucede si, cuando el usuario solicita eliminar el recurso de la aplicación anterior,
Thing2
no puede manejar adecuadamente dicho evento en:... y bajo cuál de estos errores probablemente quedaría atrapado durante las pruebas, y cuál no y volaría bajo el radar como un insecto de caza furtivo. La propiedad compartida es, más a menudo que la mayoría, una idea sin sentido.
fuente
Un ejemplo muy ilustrativo de referencias débiles utilizadas con buenos resultados es la ConditionalWeakTable , que es utilizada por el DLR (entre otros lugares) para adjuntar "miembros" adicionales a los objetos.
No quieres que la mesa mantenga vivo el objeto. Este concepto simplemente no podría funcionar sin referencias débiles.
Pero me parece que todos los usos de las referencias débiles llegaron mucho después de que se agregaron al lenguaje, ya que las referencias débiles han sido parte de .NET desde la versión 1.1. Simplemente parece algo que le gustaría agregar, de modo que la falta de destrucción determinista no lo arrinconará en lo que respecta a las características del lenguaje.
fuente
WeakReference
tipo, ya que la situación es mucho más compleja. Utiliza diferentes funcionalidades expuestas por el CLR.Si tiene implementada la capa de caché con C #, es mucho mejor colocar sus datos en la caché como referencias débiles, podría ayudar a mejorar el rendimiento de la capa de caché.
Piense que ese enfoque también podría aplicarse a la implementación de la sesión. Debido a que la sesión es un objeto de larga vida la mayor parte del tiempo, podría ser algún caso cuando no tiene memoria para un nuevo usuario. En ese caso, será mucho mejor eliminar otro objeto de sesión de usuario y luego lanzar OutOfMemoryException.
Además, si tiene un objeto grande en su aplicación (alguna tabla de búsqueda grande, etc.), eso debería usarse muy raramente y recrear dicho objeto no es un procedimiento muy costoso. Entonces mejor que sea una referencia semanal para tener una manera de liberar tu memoria cuando realmente lo necesites.
fuente