¿Cuándo utilizarías un WeakHashMap o un WeakReference?

163

El uso de referencias débiles es algo de lo que nunca he visto una implementación, así que estoy tratando de averiguar cuál es el caso de uso para ellos y cómo funcionaría la implementación. ¿Cuándo ha necesitado usar un WeakHashMapo WeakReferencey cómo se usó?

18 Conejo
fuente

Respuestas:

96

Un problema con las referencias fuertes es el almacenamiento en caché, en particular con estructuras muy grandes como las imágenes. Supongamos que tiene una aplicación que tiene que trabajar con imágenes proporcionadas por el usuario, como la herramienta de diseño de sitios web en la que trabajo. Naturalmente, desea almacenar en caché estas imágenes, porque cargarlas desde el disco es muy costoso y desea evitar la posibilidad de tener dos copias de la imagen (potencialmente gigantesca) en la memoria a la vez.

Debido a que se supone que un caché de imágenes nos impide volver a cargar imágenes cuando no es absolutamente necesario, se dará cuenta rápidamente de que el caché siempre debe contener una referencia a cualquier imagen que ya esté en la memoria. Sin embargo, con referencias fuertes ordinarias, esa referencia en sí misma forzará a la imagen a permanecer en la memoria, lo que requiere que de alguna manera determine cuándo la imagen ya no es necesaria en la memoria y la elimine de la caché, para que sea elegible para la recolección de basura. Se ve obligado a duplicar el comportamiento del recolector de basura y determinar manualmente si un objeto debe estar en la memoria.

Comprender las referencias débiles , Ethan Nicholas

Jacob Krall
fuente
43
SoftReferences no sería mejor en este caso, es decir, referencias que solo se recopilan cuando la memoria comienza a agotarse.
JesperE
Estoy un poco confundido ... digamos que tengo un caché de imágenes SWT. Las imágenes SWT deben DESECHARSE a través del método dispose () para liberar Recursos SO. Si uso un WeakHashMap para almacenarlos, mostrar exactamente ¿El GC eliminará el objeto?
marcolopes
2
@marcolopes el GC usaría un finalizador en cualquier otro objeto. Parece que a SWT no le gusta cuando haces eso, así que no creo que puedas administrar los recursos del sistema operativo con un WeakHashMap.
Jacob Krall
@marcolopes, (supondré que su GC garantiza una llamada para finalizar antes de reclamar memoria). Si la eliminación se realiza en el finalizador de caché, todo está bien. Si disponer es algo a lo que tiene que llamar manualmente, entonces 1) extienda la clase y coloque dispose en el finalizador, o 2) use phantomreference para rastrear y ejecutar dispose en consecuencia. La opción 2 es mejor (evita errores de resurrección y le da la posibilidad de ejecutar dispose en otro hilo), pero la opción 1 es más fácil de implementar sin clases auxiliares.
Pacerier
55

WeakReference versus SoftReference

Una distinción para ser clara es la diferencia entre a WeakReferencey a SoftReference.

Básicamente una WeakReferencehabrá GC-d por la JVM con impaciencia, una vez que el objeto referenciado no tiene duras referencias a ella. Un SoftReferenceobjeto d, por otro lado, tenderá a ser dejado por el recolector de basura hasta que realmente necesite recuperar la memoria.

Una memoria caché donde los valores se mantienen dentro de WeakReferences sería bastante inútil (en a WeakHashMap, son las claves las que están referenciadas débilmente). SoftReferencesson útiles para ajustar los valores cuando se desea implementar un caché que puede crecer y reducirse con la memoria disponible.

oxbow_lakes
fuente
44
"Un caché donde los valores se mantienen dentro de WeakReferences sería bastante inútil" Estoy totalmente en desacuerdo.
Thomas Eding
55
@trinithis - erm, realmente no sé qué decir. ¿Por qué un caché cuyos valores desaparecen en el momento en que no los está haciendo referencia es algo útil , exactamente?
oxbow_lakes
44
Para algo como la memorización, un caché que contenga libremente sus valores en caché podría ser útil.
Thomas Eding
55
@ThomasEding Todavía no lo entiendo. El único momento en que un caché parece útil es cuando no hay otras referencias a él ... Si tiene referencias, ¿para qué necesita un caché?
Cruncher
2
@ThomasEding, Softref le dice al entorno "almacene esto hasta que no tenga memoria". Weakref le dice al entorno "almacene esto hasta que se ejecute GC". Francamente, no hay ningún caso de uso para débilref a menos que esté depurando / perfilando el GC en sí. Si desea una memoria caché sensible a la memoria, use softref. Si no quieres un caché, ¡no lo guardes! ¿Dónde entra debilidad?
Pacerier
30

Un uso común de WeakReferencesys WeakHashMapen particular es para agregar propiedades a los objetos. Ocasionalmente, desea agregar alguna funcionalidad o datos a un objeto, pero la subclasificación y / o composición no son una opción, en ese caso, lo más obvio sería crear un hashmap que vincule el objeto que desea extender a la propiedad que desea agregar. . entonces, cuando necesite la propiedad, puede buscarla en el mapa. Sin embargo, si los objetos a los que está agregando propiedades tienden a destruirse y crearse mucho, puede terminar con una gran cantidad de objetos antiguos en su mapa que ocupan mucha memoria.

Si usa un WeakHashMaplugar, los objetos dejarán su mapa tan pronto como ya no los use el resto de su programa, que es el comportamiento deseado.

Tenía que hacer esto para añadir algunos datos para java.awt.Componentmoverse por un cambio en el JRE entre 1.4.2 y 1.5, podría haber arreglado subclasificando todos los componentes que estaba interesado int ( JButton, JFrame, JPanel....) pero esto fue mucho más fácil con mucho menos código.

luke
fuente
1
¿Cómo sabe WeakHashMap que "ya no los usa el resto de su programa"?
Vinoth Kumar CM
2
Un hasmap débil utiliza referencias débiles para sus claves. Cuando solo se hace referencia a un objeto a través de referencias débiles, el recolector de basura 'notificará' al propietario la referencia débil (en este caso, el WeaHashMap). Leería sobre WeakReferences y ReferenceQueues en los javadocs para comprender cómo interactúan estos objetos con el recolector de basura.
luke
1
gracias Luke, ¿puedes proporcionar un código simple para tu descripción?
agua hervida
Por lo tanto, la referencia débil solo tiene sentido en Java debido a la peculiar API de Java donde algunas clases no se pueden extender.
Pacerier
22

Otro caso útil para WeakHashMapy WeakReferencees una implementación de registro de escucha .

Cuando crea algo que quiere escuchar ciertos eventos, generalmente registra un oyente, por ejemplo

manager.registerListener(myListenerImpl);

Si manageralmacena su oyente con a WeakReference, eso significa que no necesita eliminar el registro, por ejemplo, con un manager.removeListener(myListenerImpl)porque se eliminará automáticamente una vez que su oyente o su componente que lo tiene no esté disponible.

Por supuesto, aún puede eliminar manualmente su escucha, pero si no lo hace o lo olvida, no causará una pérdida de memoria y no evitará que su escucha se recolecte basura.

¿Dónde WeakHashMapentra en escena?

El registro de oyentes que desea almacenar oyentes registrados como WeakReferences necesita una colección para almacenar estas referencias. No hay WeakHashSetimplementación en la biblioteca estándar de Java solo a WeakHashMappero podemos usar fácilmente la última para "implementar" la funcionalidad de la primera:

Set<ListenerType> listenerSet =
    Collections.newSetFromMap(new WeakHashMap<ListenerType, Boolean>());

Con esto listenerSetpara registrar un nuevo oyente, solo tiene que agregarlo al conjunto, e incluso si no se elimina explícitamente, si el oyente ya no está referenciado, la JVM lo eliminará automáticamente.

icza
fuente
10
El problema con el uso de weakHashSets para la lista de escucha es que las instancias de escucha anónimas creadas en register () se perderán fácilmente, lo que sería inesperado por el usuario. Es más seguro tener referencias más fuertes a los oyentes de los gerentes, y confiar en que la persona que llama haga lo correcto.
Gunanaresh
Para la implementación del registro de oyentes: ¿Todos los oyentes registrados serán recolectados / destruidos en el próximo lanzamiento de GC? Por ejemplo, mientras dispara el método onSomethingHappened () de todos los oyentes, ¿qué sucede si GC pateó?
blackkara
@icza, no puedo creer que la gente siga vendiendo este mito. Esta es una respuesta completamente incorrecta. También podría decir que otro caso útil WeakHashMapes cuando necesita un HashMapobjeto. Así wow usted no tiene que hacer manualmente hashmap.remove vez porque los artículos se retiran una vez que el obj automagicamente está fuera del alcance! ¡Literalmente mágico! Un truco mágico tan feo es un facepalm completo .
Pacerier
3
@Pacerier: he seguido sus enlaces desde otros comentarios en JavaScript hasta aquí, y todavía no entiendo por qué la implementación del registro de escucha con WeakMap es un mito. Por ejemplo, si los clientes de WebSocket deberían estar vinculados con algunos oyentes a través del servicio de registro, parece lógico almacenar objetos de socket como claves en WeakMap (para evitar que se cuelguen en la memoria después de cerrar la conexión, por ejemplo, por error) y poder para recuperar a todos sus oyentes si es necesario. Entonces, ¿podría decir, qué es exactamente lo que está mal con ese enfoque?
Dañado Organic
1
@Pacerier Yo tampoco entiendo tu objeción. En un escenario Publish-Subscribe o Event Bus, una colección de referencias débiles tiene mucho sentido para mí. Se permite que el objeto suscrito salga del alcance y se dirija a la recolección de basura sin necesidad de darse de baja formalmente. Ese proceso de cancelación de la suscripción puede ser especialmente complicado si un objeto de terceros fue responsable de la suscripción inicial sin el conocimiento del objeto suscrito. Una colección de WeakReferencesimplifica en gran medida la base del código y evita errores innecesarios relacionados con no darse de baja. ¿Qué inconveniente?
Basil Bourque
5

Esta publicación de blog muestra el uso de ambas clases: Java: sincronización en una ID . El uso es algo como esto:

private static IdMutexProvider MUTEX_PROVIDER = new IdMutexProvider();

public void performTask(String resourceId) {
    IdMutexProvider.Mutex mutext = MUTEX_PROVIDER.getMutex(resourceId);
    synchronized (mutext) {
        // look up the resource and do something with it
    }
}

IdMutextProvider proporciona objetos basados ​​en id para sincronizar. Los requisitos son:

  • debe devolver una referencia al mismo objeto para el uso concurrente de ID equivalentes
  • debe devolver un objeto diferente para diferentes ID
  • sin mecanismo de liberación (los objetos no se devuelven al proveedor)
  • no debe tener fugas (los objetos no utilizados son elegibles para la recolección de basura)

Esto se logra utilizando un mapa de almacenamiento interno de tipo:

WeakHashMap<Mutex, WeakReference<Mutex>>

El objeto es clave y valor. Cuando nada externo al mapa tiene una referencia dura al objeto, puede ser basura recolectada. Los valores en el mapa se almacenan con referencias duras, por lo que el valor debe estar envuelto en una WeakReference para evitar una pérdida de memoria. Este último punto está cubierto en el javadoc .

McDowell
fuente
3

Si, por ejemplo, desea realizar un seguimiento de todos los objetos creados de una determinada clase. Para permitir que estos objetos sean recolectados de basura, mantenga una lista / mapa de referencias débiles a los objetos en lugar de los objetos en sí.

Ahora, si alguien pudiera explicarme referencias fantasmas, sería feliz ...

JesperE
fuente
2
Un uso: PhantomReferences le permite determinar exactamente cuándo se eliminó un objeto de la memoria. De hecho, son la única forma de determinar eso. ( weblogs.java.net/blog/enicholas/archive/2006/05/… )
Jacob Krall
En realidad, no se elimina hasta que lo borre explícitamente. "A diferencia de las referencias débiles y débiles, el recolector de basura no borra automáticamente las referencias fantasmas a medida que se colocan en cola. Un objeto al que se puede acceder mediante referencias fantasmas permanecerá así hasta que se borren todas esas referencias o se vuelvan inalcanzables".
jontro
@ jontro, pero ya se ha finalizado , todos los miembros se han ido. Efectivamente, es un obj en blanco. Consulte stackoverflow.com/q/7048767/632951
Pacerier el
3

Como se indicó anteriormente, las referencias débiles se mantienen mientras exista una referencia fuerte.

Un ejemplo de uso sería usar WeakReference dentro de los oyentes, para que los oyentes ya no estén activos una vez que la referencia principal a su objeto de destino desaparezca. Tenga en cuenta que esto no significa que WeakReference se elimine de la lista de oyentes, la limpieza aún es necesaria, pero se puede realizar, por ejemplo, a horas programadas. Esto también tiene el efecto de evitar que el objeto escuchado contenga fuertes referencias y eventualmente sea una fuente de hinchazón de memoria. Ejemplo: componentes de la interfaz gráfica de usuario de Swing que hacen referencia a un modelo que tiene un ciclo de vida más largo que la ventana.

Mientras jugaba con los oyentes como se describió anteriormente, nos dimos cuenta rápidamente de que los objetos se recogen "inmediatamente" desde el punto de vista del usuario.

Louis Jacomet
fuente
Gracias respuesta útil. Pero me pregunto, en ese caso, ¿deberían los oyentes estar fuertemente registrados (referenciados)?
blackkara
Esta respuesta es completamente incorrecta. Elaboración: stackoverflow.com/questions/154724/…
Pacerier
@Pacerier: ¡ WeakReferencestu comentario está completamente equivocado!
2

Un uso del mundo real que tuve para WeakReferences es si tienes un solo objeto muy grande que rara vez se usa. No desea mantenerlo en la memoria cuando no es necesario; pero, si otro hilo necesita el mismo objeto, tampoco querrás dos de ellos en la memoria. Puede mantener una referencia débil al objeto en alguna parte, y referencias duras en los métodos que lo utilizan; cuando ambos métodos terminen, el objeto será recogido.


fuente
1
Esta es una softreferencia, no una referencia débil. Ver stackoverflow.com/a/155492/632951
Pacerier el
-1

puede usar weakhashmap para implementar un almacenamiento en caché sin recursos para la creación de objetos expansivos.

pero tenga en cuenta que no es deseable tener objetos mutables. Lo usé para almacenar en caché los resultados de la consulta (que tardan unos 400 ms en ejecutarse) en un motor de búsqueda de texto, que rara vez se actualiza.

Andreas Petersson
fuente
Estás hablando de una softreferencia, no de una referencia débil. Ver stackoverflow.com/a/155492/632951
Pacerier el