¿Cuál es la diferencia entre SoftReference y WeakReference en Java?

Respuestas:

927

De Comprender las referencias débiles , de Ethan Nicholas:

Referencias débiles

Una referencia débil , simplemente, es una referencia que no es lo suficientemente fuerte como para obligar a un objeto a permanecer en la memoria. Las referencias débiles le permiten aprovechar la capacidad del recolector de basura para determinar la accesibilidad para usted, por lo que no tiene que hacerlo usted mismo. Crea una referencia débil como esta:

WeakReference weakWidget = new WeakReference(widget);

y luego en otra parte del código que puede usar weakWidget.get()para obtener el Widgetobjeto real . Por supuesto, la referencia débil no es lo suficientemente fuerte como para evitar la recolección de basura, por lo que puede encontrar (si no hay referencias fuertes al widget) que de weakWidget.get()repente comienza a regresar null.

...

Referencias suaves

UNA referencia suave es exactamente como una referencia débil, excepto que está menos ansiosa por tirar el objeto al que se refiere. Un objeto al que solo se puede llegar débilmente (las referencias más fuertes a él WeakReferences) se descartarán en el próximo ciclo de recolección de basura, pero un objeto al que se puede acceder suavemente generalmente se quedará por un tiempo.

SoftReferencesno están obligados a comportarse de manera diferente a WeakReferences , pero en la práctica, los objetos de alcance suave generalmente se retienen siempre que la memoria sea abundante. Esto los convierte en una base excelente para un caché, como el caché de imágenes descrito anteriormente, ya que puede dejar que el recolector de basura se preocupe tanto por lo accesible que son los objetos (un objeto fuertemente accesible nunca se eliminará del caché) y lo mal que necesita la memoria que están consumiendo.

Y Peter Kessler agregó en un comentario:

Sun JRE trata las SoftReferences de manera diferente a WeakReferences. Intentamos aferrarnos al objeto al que hace referencia SoftReference si no hay presión en la memoria disponible. Un detalle: la política para los JRE "-client" y "-server" es diferente: el JRE -client intenta mantener su huella pequeña prefiriendo borrar SoftReferences en lugar de expandir el montón, mientras que el JRE -server intenta mantener su alto rendimiento al preferir expandir el montón (si es posible) en lugar de borrar SoftReferences. Una talla no sirve para todos.

Michael Myers
fuente
77
No publique más, lo puede encontrar en la máquina de wayback: web.archive.org/web/20061130103858/http://weblogs.java.net/blog/…
riccardo.tasso
esta vez, el archivo ya no está disponible
usuario1506104
209

Las referencias débiles se recopilan con entusiasmo. Si GC encuentra que un objeto es débilmente accesible (accesible solo a través de referencias débiles), borrará las referencias débiles a ese objeto inmediatamente. Como tal, son buenos para mantener una referencia a un objeto para el cual su programa también guarda (fuertemente referenciada) "información asociada" en algún momento, como información de reflexión en caché sobre una clase, o un contenedor para un objeto, etc. Cualquier cosa que haga no tiene sentido guardarlo después de que el objeto al que está asociado sea GC-ed. Cuando la referencia débil se borra, se pone en cola en una cola de referencia que su código sondea en algún lugar, y también descarta los objetos asociados. Es decir, mantiene información adicional sobre un objeto, pero esa información no es necesaria una vez que el objeto al que hace referencia desaparece. Realmente, en ciertas situaciones, incluso puede subclasificar WeakReference y mantener la información adicional asociada sobre el objeto en los campos de la subclase WeakReference. Otro uso típico de WeakReference es en conjunción con Maps para mantener instancias canónicas.

SoftReferences, por otro lado, son buenos para almacenar en caché recursos externos y recreables, ya que el GC generalmente demora en borrarlos. Sin embargo, se garantiza que todas las SoftReferences se borrarán antes de que se arroje OutOfMemoryError, por lo que en teoría no pueden causar un OOME [*].

Un ejemplo de caso de uso típico es mantener una forma analizada de un contenido de un archivo. Implementaría un sistema donde cargaría un archivo, lo analizaría y mantendría una SoftReference al objeto raíz de la representación analizada. La próxima vez que necesite el archivo, intentará recuperarlo a través de SoftReference. Si puede recuperarlo, se ahorró otra carga / análisis, y si el GC lo borró mientras tanto, lo vuelve a cargar. De esa manera, utiliza memoria libre para la optimización del rendimiento, pero no se arriesga a un OOME.

Ahora para el [*]. Mantener una SoftReference no puede causar un OOME en sí mismo. Si, por otro lado, utiliza por error SoftReference para una tarea, se debe utilizar una WeakReference (es decir, si mantiene la información asociada a un Objeto fuertemente referenciada y la descarta cuando el objeto de Referencia se borra), puede ejecutar OOME como su código que sondea el ReferenceQueue y descarta los objetos asociados podría no ejecutarse de manera oportuna.

Por lo tanto, la decisión depende del uso: si está almacenando en caché información que es costosa de construir, pero no obstante reconstruible a partir de otros datos, use referencias suaves, si mantiene una referencia a una instancia canónica de algunos datos, o si desea tenga una referencia a un objeto sin "poseerlo" (evitando así que sea GC'd), use una referencia débil.

driekken
fuente
14
Particularmente útil para la explicación de cuándo se usarían los objetos débiles.
Jack BeNimble
Un punto clave sobre el uso adecuado de a WeakReferencees que en los lugares donde uno debería usarlo, el hecho de que uno pueda permanecer válido durante un tiempo después de que la referencia salga del alcance puede ser tolerable, pero no es deseable.
supercat
Me cuesta entender de qué sirve WeakHashMap si siempre produce una referencia débil a su objeto de valor clave.
@supercat, solo hay un uso apropiado del WeakReferencecual es observar las ejecuciones de GC. Ver elaboración: stackoverflow.com/a/46291143/632951
Pacerier
1
@Pacerier: El autor de esa publicación simplemente está equivocado. Descuida algunos otros escenarios de uso, como la suscripción a eventos, su segundo punto no tiene sentido y su tercer punto supone que un programador puede hacer cosas que pueden no ser posibles. Su primer punto es razonable, pero se relaciona directamente con lo que dije. Si el código frecuentemente tiene que construir y comparar grandes objetos inmutables, por ejemplo, la parte de construcción a menudo será más barata si el código crea nuevos objetos sin tener en cuenta si ya existen, pero será una comparación entre un objeto y sí mismo (referencias idénticas) ...
supercat
155

En java ; orden de más fuerte a más débil, hay: fuerte, suave, débil y fantasma

Una referencia fuerte es una referencia normal que protege el objeto referido de la colección por GC. es decir, nunca se acumula basura.

Una referencia suave es elegible para la recolección por parte del recolector de basura, pero probablemente no se recolectará hasta que se necesite su memoria. es decir, la basura se acumula antes OutOfMemoryError.

Una referencia débil es una referencia que no protege un objeto referenciado de la colección por GC. es decir, la basura se acumula cuando no hay referencias fuertes o suaves.

Una referencia fantasma es una referencia a un objeto al que se hace referencia fantasma una vez que se ha finalizado, pero antes de que se haya recuperado su memoria asignada.

Fuente

Analogía: suponga que una JVM es un reino, Object es un rey del reino y GC es un atacante del reino que intenta matar al rey (objeto).

  • Cuando el rey es fuerte , GC no puede matarlo.
  • Cuando el rey es Blando , GC lo ataca pero King gobierna el reino con protección hasta que los recursos estén disponibles.
  • Cuando el rey es Débil , GC lo ataca pero gobierna el reino sin protección.
  • Cuando el rey es Fantasma , GC ya lo mató, pero el rey está disponible a través de su alma.
Premraj
fuente
77
Referencia suave ... until memory is availableno tiene sentido. Qué quiere decir is eligible for collection by garbage collector, but probably won't be collected until its memory is needed for another use?
ToolmakerSteve
1
Sí, el recolector de basura no recopilará la referencia hasta que la memoria esté disponible.
Premraj
2
Me gustan las cosas simples explicadas, ¡sin demasiado bla bla bla +1 de mi parte!
Adelin
3
Excelente resumen con ejemplo innovador
Ravindra babu
+1, lectura adicional: javarevisited.blogspot.in/2014/03/…
roottraveller
77

Referencia débil http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/ref/WeakReference.html

Principio: weak reference está relacionado con la recolección de basura. Normalmente, los objetos que tienen uno o más referenceno serán elegibles para la recolección de basura.
El principio anterior no es aplicable cuando lo es weak reference. Si un objeto solo tiene una referencia débil con otros objetos, entonces está listo para la recolección de basura.

Veamos el siguiente ejemplo: Tenemos un Mapcon objetos donde Key es referencia a un objeto.

import java.util.HashMap;   
public class Test {

    public static void main(String args[]) {
        HashMap<Employee, EmployeeVal> aMap = new 
                       HashMap<Employee, EmployeeVal>();

        Employee emp = new Employee("Vinoth");
        EmployeeVal val = new EmployeeVal("Programmer");

        aMap.put(emp, val);

        emp = null;

        System.gc();
        System.out.println("Size of Map" + aMap.size());

    }
}

Ahora, durante la ejecución del programa que hemos realizado emp = null. Mantener Mappresionada la tecla no tiene sentido aquí tal como es null. En la situación anterior, el objeto no es basura recolectada.

WeakHashMap

WeakHashMapes uno donde las entradas ( key-to-value mappings) se eliminarán cuando ya no sea posible recuperarlas del Map.

Permítanme mostrar el mismo ejemplo anterior con WeakHashMap

import java.util.WeakHashMap;

public class Test {

    public static void main(String args[]) {
        WeakHashMap<Employee, EmployeeVal> aMap = 
                    new WeakHashMap<Employee, EmployeeVal>();

        Employee emp = new Employee("Vinoth");
        EmployeeVal val = new EmployeeVal("Programmer");

        aMap.put(emp, val);

        emp = null;

        System.gc();
        int count = 0;
        while (0 != aMap.size()) {
            ++count;
            System.gc();
        }
        System.out.println("Took " + count
                + " calls to System.gc() to result in weakHashMap size of : "
                + aMap.size());
    }
}

Salida: Tomó 20 calls to System.gc()como resultado en aMap size: 0.

WeakHashMapsolo tiene referencias débiles a las teclas, no referencias fuertes como otras Mapclases. Hay situaciones que debe tener cuidado cuando el valor o la clave están fuertemente referenciados a pesar de haberlo utilizado WeakHashMap. Esto se puede evitar envolviendo el objeto en una WeakReference .

import java.lang.ref.WeakReference;
import java.util.HashMap;

public class Test {

    public static void main(String args[]) {
        HashMap<Employee, EmployeeVal> map = 
                      new HashMap<Employee, EmployeeVal>();
        WeakReference<HashMap<Employee, EmployeeVal>> aMap = 
                       new WeakReference<HashMap<Employee, EmployeeVal>>(
                map);

        map = null;

        while (null != aMap.get()) {
            aMap.get().put(new Employee("Vinoth"),
                    new EmployeeVal("Programmer"));
            System.out.println("Size of aMap " + aMap.get().size());
            System.gc();
        }
        System.out.println("Its garbage collected");
    }
}

Referencias suaves

Soft ReferenceEs ligeramente más fuerte que la referencia débil. La referencia suave permite la recolección de basura, pero le pide al recolector de basura que la limpie solo si no hay otra opción.

El recolector de basura no recolecta de manera agresiva los objetos que se pueden alcanzar suavemente como lo hace con los que se pueden alcanzar débilmente; en cambio, solo recolecta los objetos que se pueden alcanzar suavemente si realmente "necesita" la memoria. Las referencias suaves son una forma de decirle al recolector de basura: "Mientras la memoria no sea demasiado apretada, me gustaría mantener este objeto cerca. Pero si la memoria se pone muy apretada, adelante, recójala y me ocuparé con ese." Se requiere que el recolector de basura borre todas las referencias suaves antes de poder lanzar OutOfMemoryError.

Thalaivar
fuente
55
Usted puede obtener una NullPointerExceptionen aMap.get().put(...).
xehpuk
Su primer ejemplo de HashMap parece incorrecto. Cuando haces "aMap.put (emp, val);" tanto 'emp' como 'val' son referencias fuertes. Internamente, se crea una nueva variable para contener 'emp' y 'val', así que cuando haces "emp = null;" solo está anulando la variable "emp", pero no la variable internamente en el mapa hash (que aún contiene el objeto Empleado original). Por lo tanto, el mapa hash seguirá teniendo una fuerte referencia a 'emp' independientemente de lo que haga con la variable 'emp' en el exterior.
Tiago
@Tiago. No. Presumiblemente por "primer ejemplo" se está refiriendo al WeakHashMapejemplo (ya que es el primero que demuestra un comportamiento Débil). Vistazo a doc para "WeakHashMap": "An entry in a WeakHashMap will automatically be removed when its key is no longer in ordinary use. " El punto de usar WeakHashMap todo es que usted no tiene que declarar / de acceso en un WeakReference; WeakHashMap lo hace internamente. docs.oracle.com/javase/7/docs/api/java/util/WeakHashMap.html
ToolmakerSteve
Tomó 0 llamadas a System.gc () para dar como resultado un tamaño débil de HashMap de: 0 ¿es la salida de su segundo programa?
Adelin
1
Para otro ejemplo de WeakHashMapen acción, con la aplicación de ejemplo que muestra cómo se eliminan las entradas solo después de que se ejecuta la recolección de basura, consulte mi Respuesta a la pregunta, ¿WeakHashMap está creciendo constantemente o borra las claves de basura? .
Basil Bourque
50

La única diferencia real entre una referencia suave y una referencia débil es que

el recolector de basura utiliza algoritmos para decidir si reclamar o no un objeto accesible de manera suave, pero siempre reclama un objeto débilmente accesible.

Samir Mangroliya
fuente
@ATorras, Samir.
Expandí
25

SoftReferenceEstá diseñado para cachés. Cuando se descubre que un objeto hace WeakReferencereferencia a un objeto inalcanzable, se borrará de inmediato. SoftReferencepuede dejarse como está. Por lo general, hay algún algoritmo relacionado con la cantidad de memoria libre y el último tiempo utilizado para determinar si se debe borrar. El algoritmo actual de Sun es borrar la referencia si no se ha utilizado en tantos segundos como haya megabytes de memoria libre en el almacenamiento dinámico de Java (configurable, el HotSpot del servidor verifica con el almacenamiento dinámico máximo establecido por -Xmx). SoftReferences se borrará antes de que OutOfMemoryErrorse lance, a menos que se pueda acceder de otra manera.

Tom Hawtin - tackline
fuente
99
Pero en Android no se recomienda para cachés developer.android.com/reference/java/lang/ref/…
Yaroslav Mytkalyk
44
@DoctororDrive tbf ¡la pregunta era sobre Java, no Dalvik! :-P
fabspro
2
@YaroslavMytkalyk, francamente, si Android quiere reescribir el comportamiento de una clase, debe usar su propio espacio de nombres, no java.lang. Tal abuso de sinónimos no le está haciendo ningún bien a nadie.
Pacerier
9

Este artículo puede ser muy útil para comprender referencias fuertes, suaves, débiles y fantasmas.


Para darle un resumen,

Si solo tienes referencias débiles a un objeto (sin referencias fuertes), entonces el objeto será reclamado por GC en el próximo ciclo de GC.

Si solo tiene referencias suaves a un objeto (sin referencias fuertes), entonces el objeto será reclamado por GC solo cuando JVM se quede sin memoria.


Entonces puedes decir que las referencias fuertes tienen el poder supremo (nunca puede ser recolectado por GC)

Las referencias suaves son poderosas que las débiles (ya que pueden escapar del ciclo de GC hasta que JVM se quede sin memoria)

Las referencias débiles son incluso menos potentes que las referencias suaves (ya que no pueden escapar de ningún ciclo de GC y se recuperarán si el objeto no tiene otra referencia fuerte).


Analogía de restaurantes

  • Camarero - GC
  • Usted - Objeto en el montón
  • Área / espacio del restaurante: espacio de almacenamiento dinámico
  • Nuevo cliente - Nuevo objeto que quiere mesa en restaurante

Ahora, si usted es un cliente fuerte (análogo a una referencia fuerte), incluso si un nuevo cliente entra en el restaurante o lo que sea que sea feliz, nunca dejará su mesa (el área de memoria en el montón). El camarero no tiene derecho a decirle (o incluso pedirle) que abandone el restaurante.

Si eres un cliente suave (análogo a una referencia flexible), si un cliente nuevo entra al restaurante, el camarero no le pedirá que abandone la mesa a menos que no quede otra mesa vacía para acomodar al nuevo cliente. (En otras palabras, el camarero le pedirá que abandone la mesa solo si un nuevo cliente interviene y no queda otra mesa para este nuevo cliente)

Si usted es un cliente débil (análogo a una referencia débil), el camarero, a su voluntad, puede (en cualquier momento) pedirle que salga del restaurante: P

La lujosa Kothari
fuente
7

La única diferencia real

Según el documento , un GC en ejecución debe eliminar las referencias débiles sueltas .

Según el documento , las SoftReferences sueltas deben borrarse antes de lanzar OOM.

Esa es la única diferencia real. Todo lo demás no es parte del contrato. (Asumiré que los últimos documentos son contractuales).

SoftReferences son útiles. Las memorias caché sensibles a la memoria usan SoftReferences, no WeakReferences.


El único uso apropiado de WeakReference es observar la ejecución del GC. Para ello, crea una nueva WeakReference cuyo objeto salga inmediatamente del alcance y luego intente salir de nulo weak_ref.get(). Cuando es así null, aprende que entre esta duración, el GC se ejecutó.

En cuanto al uso incorrecto de WeakReference, la lista es interminable:

  • un truco pésimo para implementar la softreferencia de prioridad 2 de modo que no tenga que escribir una, pero no funciona como se esperaba porque la memoria caché se borrará en cada ejecución del GC, incluso cuando haya memoria de sobra. Consulte https://stackoverflow.com/a/3243242/632951 para ver las copias. (Además, ¿qué pasa si necesita más de 2 niveles de prioridad de caché? Aún necesitaría una biblioteca real para ello).

  • un pésimo truco para asociar datos con un objeto de una clase existente, pero crea una pérdida de memoria (OutOfMemoryError) cuando su GC decide tomarse un descanso después de crear sus referencias débiles. Además, es más que feo: un mejor enfoque es usar tuplas.

  • un pésimo truco para asociar datos con un objeto de una clase existente, donde la clase tiene el descaro de no poder subclasearse, y se usa en un código de función existente al que debe llamar. En tal caso, la solución adecuada es editar la clase y hacer que sea subclase, o editar la función y hacer que tome una interfaz en lugar de una clase, o use una función alternativa.

Pacerier
fuente
¿Qué pasa con un caché donde el tipo de clave equals()es solo identidad de objeto? Las referencias suaves parecen un desperdicio allí, porque una vez que un objeto clave ya no es fácilmente accesible, nadie volverá a buscar esa asignación nuevamente.
HighCommander4
Estoy en desacuerdo. Use WeakReference cuando no quiera influir en GC de todos modos (es posible que desee guardar una referencia de objeto y luego verifique si aún existe, sin ninguna preferencia). Utilice SoftReference si desea influir en el GC para tratar de mantener el objeto (es decir, cuando prefiere que el GC lo mantenga).
David Refaeli el
Un buen ejemplo de dónde usar WeakReference es en AsyncTask de Android, para mantener una instancia del contexto. De esa manera, si el contexto muere (si la actividad es la rotación de la pantalla, etc.), AsyncTask no tendrá ninguna referencia fuerte, por lo que se puede recolectar basura. Compruebe youtu.be/…
David Refaeli
3

Los seis tipos de estados de accesibilidad de objetos en Java:

  1. Fuertes objetos alcanzables Ly - GC se no cobro revertido ( reclamar la memoria ocupada por ) esta clase de objeto. Estos son accesibles a través de un nodo raíz u otro objeto fuertemente accesible (es decir, a través de variables locales, variables de clase, variables de instancia, etc.)
  2. Soft Ly objetos alcanzables - GC pueden intentar recoger este tipo de objetos en función de la contención de memoria. Estos son accesibles desde la raíz a través de uno o más objetos de referencia blandos
  3. La debilidad de los objetos alcanzables Ly - GC deben recoger este tipo de objeto. Estos son accesibles desde la raíz a través de uno o más objetos de referencia débiles
  4. Objetos resucitables : GC ya está en proceso de recopilar estos objetos. Pero pueden volver a uno de los estados: Fuerte / Suave / Débil por la ejecución de algún finalizador
  5. FantasmaObjeto accesible: GC ya está en el proceso de recopilar estos objetos y ha decidido no ser resucitable por ningún finalizador (si declara un método finalize (), entonces su finalizador se habrá ejecutado) . Estos son accesibles desde la raíz a través de uno o más objetos de referencia fantasma
  6. Objeto inalcanzable : un objeto no es fuerte, suave, débil ni fantasma accesible, y no es resucitable. Estos objetos están listos para su recuperación.

Para más detalles: https://www.artima.com/insidejvm/ed2/gc16.html «collapse

V.Vidyasagar
fuente
44
No es una buena descripción de referencias fantasmas. Además, ha enumerado los 4 tipos en un orden peculiar. "fantasma" es el tipo más débil, no el más fuerte. El orden tradicional para enumerar estos es "fuerte, suave, débil, fantasma". Y no tengo idea de dónde sacaste la idea de que los objetos fantasmas se usan para los mecanismos de almacenamiento en caché. AFAIK, son un estado temporal visto solo por el GC, no algo con lo que un programador ordinario trabajaría.
ToolmakerSteve
@ToolmakerSteve y todo: disculpas por un par de cosas 1. la explicación incorrecta de las referencias de Phantom en la versión anterior de mi respuesta, y 2. Retraso en la corrección de los errores. Ahora la respuesta se ha mejorado al corregir los errores
V.Vidyasagar
1

Se debe tener en cuenta que un objeto débilmente referenciado solo se recopilará cuando SOLO tenga referencias débiles. Si tiene tanto como una referencia fuerte, no se recopilará sin importar cuántas referencias débiles tenga.

Fai Lau
fuente
Esto es sentido común ... lo mismo ocurre con softref y phantomref.
Pacerier
1

Para dar un aspecto de uso de memoria en acción, realicé un experimento con referencias Strong, Soft, Weak & Phantom bajo carga pesada con objetos pesados ​​al retenerlos hasta el final del programa. Luego monitoreó el uso del montón y el comportamiento del GC . Estas métricas pueden variar caso por caso, pero seguramente brindan una comprensión de alto nivel. A continuación se encuentran los hallazgos.

Comportamiento de montón y GC bajo carga pesada

  • Referencia fuerte / dura : a medida que el programa continuó, JVM no pudo recopilar el objeto referenciado fuerte retenido. Finalmente terminó en "java.lang.OutOfMemoryError: espacio de almacenamiento dinámico de Java"
  • Referencia suave : a medida que el programa continuó, el uso del almacenamiento dinámico siguió creciendo, pero OLD gen GC ocurrió cuando se acercaba al almacenamiento dinámico máximo. GC comenzó un poco más tarde en el tiempo después de iniciar el programa.
  • Referencia débil : cuando comenzó el programa, los objetos comenzaron a finalizar y a recopilarse casi de inmediato. La mayoría de los objetos se recolectaron en la recolección de basura de la generación joven.
  • Referencia fantasma : similar a la referencia débil, los objetos con referencia fantasma también comenzaron a finalizarse y la basura se recogió de inmediato. No había GC de la generación anterior y todos los objetos se recolectaban en la recolección de basura de la generación joven.

Puede obtener gráficos más detallados , estadísticas, observaciones para este experimento aquí .

Ravi K
fuente
0

WeakReference : los objetos a los que solo se hace referencia débilmente se recopilan en cada ciclo de GC (menor o completo).

SoftReference : cuando los objetos a los que solo se hace una referencia suave se recopilan depende de:

  1. -XX: SoftRefLRUPolicyMSPerMB = N flag (el valor predeterminado es 1000, también conocido como 1 segundo)

  2. Cantidad de memoria libre en el montón.

    Ejemplo:

    • el almacenamiento dinámico tiene 10 MB de espacio libre (después de GC completo);
    • -XX: SoftRefLRUPolicyMSPerMB = 1000

    Luego, el objeto al que hace referencia SoftReference solo se recopilará si la última vez que se accedió es mayor de 10 segundos.

Artem Petrov
fuente