java caching jvm soft-references

java - ¿Cómo se recogen los SoftReferences por las JVM en la práctica?



caching soft-references (5)

Tengo dos cachés independientes que se ejecutan en una JVM (una controlada por una biblioteca de terceros), cada una con referencias blandas. Preferiría que la JVM borre mi caché controlado antes que el controlado por la biblioteca. El javadoc de SoftReference establece:

Se garantiza que todas las referencias blandas a los objetos accesibles se borrarán antes de que la máquina virtual lance un OutOfMemoryError. De lo contrario, no se colocan restricciones sobre el momento en que se borrará una referencia suave o el orden en que se borrará un conjunto de tales referencias a diferentes objetos. Sin embargo, se alienta a las implementaciones de máquinas virtuales a sesgar contra el borrado de las referencias de software creadas recientemente o utilizadas recientemente.

Las instancias directas de esta clase pueden usarse para implementar cachés simples; esta clase o subclases derivadas también se pueden usar en estructuras de datos más grandes para implementar cachés más sofisticados. Siempre que el referente de una referencia suave sea fuertemente accesible, es decir, esté realmente en uso, la referencia suave no se borrará. Por lo tanto, una memoria caché sofisticada puede, por ejemplo, evitar que se descarten las entradas que se usaron más recientemente al mantener fuertes referencias a esas entradas, dejando las entradas restantes a ser descartadas a criterio del recolector de basura.

¿De qué manera las implementaciones comunes de JVM, especialmente HotSpot, manejan SoftReferences en la práctica? ¿Tienen "sesgo en contra de la eliminación de referencias blandas creadas recientemente o usadas recientemente" como lo recomiendan las especificaciones?


Cualquiera sea la respuesta, confiar en una estrategia en particular haría que su software no sea confiable porque cada implementación de JVM puede ser diferente. Incluso para una JVM dada, configurarlo de manera diferente puede alterar la estrategia exacta y romper su software. En resumen, es un error confiar en una estrategia particular.

¿Qué tipo de recurso gestiona tu caché? Si se trata de un objeto asignado de pila pura, la estrategia no debería importar. Sin embargo, el uso de una ReferenceQueue puede ayudarlo a recibir una notificación cuando se borra una SoftReference.

Si el tipo de recurso no es solo un objeto asignado en el montón, entonces debe solicitar a sus usuarios que llamen a un método de lanzamiento explícito, es decir, Closeable.close (). Para protegerse contra las llamadas "olvidadas" a este método de lanzamiento, puede considerar implementar un método finalize (), pero tenga cuidado con sus efectos secundarios. Para obtener más información sobre esto, recomiendo leer el "Artículo 7: Evitar los finalizadores" de "Effective Java (2nd Edition)" de Joshua Bloch.


Encontré una información en una pregunta frecuente sobre HotSpot, que puede estar desactualizada: http://www.oracle.com/technetwork/java/hotspotfaq-138619.html#gc_softrefs

¿Qué determina cuándo se enjuagan los objetos de referencia suave?

A partir de 1.3.1, los objetos de fácil acceso permanecerán vivos durante un período de tiempo posterior a la última vez que se hizo referencia. El valor predeterminado es un segundo de vida útil por megabyte libre en el montón. Este valor se puede ajustar utilizando el indicador -XX: SoftRefLRUPolicyMSPerMB, que acepta valores enteros que representan milisegundos. Por ejemplo, para cambiar el valor de un segundo a 2,5 segundos, use este indicador:

-XX: SoftRefLRUPolicyMSPerMB = 2500

La máquina virtual Java HotSpot Server utiliza el tamaño máximo posible de almacenamiento dinámico (como se establece con la opción -Xmx) para calcular el espacio libre restante.

La máquina virtual Java Hotspot Client utiliza el tamaño del montón actual para calcular el espacio libre.

Esto significa que la tendencia general es que la VM del servidor haga crecer el montón en lugar de vaciar las referencias blandas, y, por lo tanto, -Xmx tiene un efecto significativo en el momento en que se recolectan las referencias blandas.

Por otro lado, el cliente VM tendrá una mayor tendencia a vaciar las referencias blandas en lugar de hacer crecer el montón.

El comportamiento descrito anteriormente es válido para las versiones 1.3.1 a través de Java SE 6 de las máquinas virtuales de Java HotSpot. Sin embargo, este comportamiento no es parte de la especificación de la máquina virtual y está sujeto a cambios en futuras versiones. Del mismo modo, no se garantiza que la marca -XX: SoftRefLRUPolicyMSPerMB esté presente en ninguna versión dada.

Antes de la versión 1.3.1, las máquinas virtuales de Java HotSpot borraron las referencias blandas cada vez que las encontraban.

Aún más detalles están disponibles en: jeremymanson.blogspot.com/2009/07/… (cortesía del comentario de MiserableVariable)


No es que esto sea autoritario, pero al usar SoftReference en la ira nunca he visto a VM para eliminarlos en lugar de aumentar el tamaño de VM. De hecho, de alguna manera asumí que ese era el caso y el diseño dependía mucho de eso. Tenía los mismos -ms y -mx pero eso no debería importar.

Pero no puedo encontrar ninguna especificación que realmente diga que esto es necesario. jeremymanson.blogspot.com/2009/07/… blog parece entrar en gran detalle sobre cómo se SoftReferences las SoftReferences . A partir de una lectura rápida, parece que pueden borrarse incluso si hay otra memoria disponible.


Sólo una lluvia de ideas. Si desea que su caché se borre antes que el otro caché, ¿quizás pueda vincular los dos? ¿Quizás manteniendo una fuerte referencia a las entradas en el segundo caché y solo liberando esas referencias cuando se borran los miembros de su propio caché?

Parece enrevesado. Probablemente me inclinaría por aceptar simplemente que ambas cachés son exactamente eso, un caché. Los fallos de caché pueden ser dolorosos para el rendimiento, pero al menos su software no tendrá una estrategia de administración de caché complicada.


Parece que podría ser sintonizable, pero no lo es. El colector de barrido y marca simultáneo se cuelga en la implementación del montón predeterminado de must_clear_all_soft_refs() que aparentemente solo es true cuando se realiza una _last_ditch_collection .

bool GenCollectedHeap::must_clear_all_soft_refs() { return _gc_cause == GCCause::_last_ditch_collection; }

Mientras que el manejo normal de la asignación fallida tiene tres llamadas sucesivas al método do_collect del montón, en el CollectorPolicy.cpp

HeapWord* GenCollectorPolicy::satisfy_failed_allocation(size_t size, bool is_tlab) {

El que trata de recopilar, intenta reasignar, intenta expandir el montón si eso falla, y luego, como último esfuerzo, intenta recopilar las referencias blandas.

El comentario sobre la última colección es bastante revelador (y el único que activa la eliminación de referencias blandas)

// If we reach this point, we''re really out of memory. Try every trick // we can to reclaim memory. Force collection of soft references. Force // a complete compaction of the heap. Any additional methods for finding // free memory should be here, especially if they are expensive. If this // attempt fails, an OOM exception will be thrown. { IntFlagSetting flag_change(MarkSweepAlwaysCompactCount, 1); // Make sure the heap is fully compacted gch->do_collection(true /* full */, true /* clear_all_soft_refs */, size /* size */, is_tlab /* is_tlab */, number_of_generations() - 1 /* max_level */); }

--- Editado en respuesta a lo obvio, describía referencias débiles, no blandas ---

En la práctica, me imagino que SoftReferences solo se siguen "no" cuando se llama a la JVM para la recolección de basura en respuesta a que intentan evitar un OutOfMemoryError .

Para que SoftReference s sea compatible con los cuatro recolectores de basura Java 1.4, y con el nuevo recolector G1, la decisión debe recaer solo en la determinación de accesibilidad. En el momento en que se producen la cosecha y la compactación, es demasiado tarde para decidir si un objeto es alcanzable. Esto sugiere (pero no requiere) que exista un "contexto" de colección que determine la accesibilidad basada en la disponibilidad de memoria libre en el montón. Dicho contexto tendría que indicar que no se siguen las SoftReference antes de intentar seguirlas.

Debido a que la OutOfMemoryError basura para evitar OutOfMemoryError está especialmente programada de una manera completa para detener el mundo, no sería difícil imaginar un escenario en el que el administrador del montón establezca un SoftReference "no seguir SoftReference " antes de que ocurra la recolección.

--- Ok, entonces decidí que una respuesta "debe funcionar de esta manera" simplemente no era lo suficientemente buena ---

Desde el código fuente src/share/vm/gc_implementation/concurrentMarkSweep/vmCMSOperations.cpp (los aspectos más destacados son míos)

La operación para realmente "hacer" la recolección de basura:

170 void VM_GenCollectFullConcurrent::doit() {

Mejor que seamos un hilo de máquina virtual, de lo contrario, un hilo de "programa" es un recolector de basura.

171 assert(Thread::current()->is_VM_thread(), "Should be VM thread");

Somos un coleccionista concurrente, ¡así que mejor que nos programen al mismo tiempo!

172 assert(GCLockerInvokesConcurrent || ExplicitGCInvokesConcurrent, "Unexpected"); 173

Agarra el montón (que tiene el objeto GCCause en él).

174 GenCollectedHeap* gch = GenCollectedHeap::heap();

Compruebe si necesitamos una colección "joven" de primer plano

175 if (_gc_count_before == gch->total_collections()) { 176 // The "full" of do_full_collection call below "forces" 177 // a collection; the second arg, 0, below ensures that 178 // only the young gen is collected. XXX In the future, 179 // we''ll probably need to have something in this interface 180 // to say do this only if we are sure we will not bail 181 // out to a full collection in this attempt, but that''s 182 // for the future.

¿Los hilos del programa no se entrometen con el montón?

183 assert(SafepointSynchronize::is_at_safepoint(), 184 "We can only be executing this arm of if at a safepoint");

Recupera la causa de la recolección de basura (el motivo de esta recopilación) del montón.

185 GCCauseSetter gccs(gch, _gc_cause);

Haz una colección completa del espacio joven.

Tenga en cuenta que sus pases en el valor del montón must_clear_all_soft_refs flag, que en un escenario OutOfMemory debe haberse establecido en verdadero, y en ambos casos dirige la "do_full_collection" para no seguir las referencias blandas

186 gch->do_full_collection(gch->must_clear_all_soft_refs(), 187 0 /* collect only youngest gen */);

La _gc_cause es una enumeración, que se establece (conjetura aquí) en _allocation_failure en el primer intento de evitar OutOfMemoryError y _last_ditch_collection después de que falla (para intentar recolectar basura transitoria)

Una mirada rápida en el módulo "montón" de memoria muestra que en do_full_collection que llama a las referencias blandas do_collection se borran explícitamente (bajo las condiciones "correctas") con la línea

480 ClearedAllSoftRefs casr(do_clear_all_soft_refs, collector_policy());

--- La publicación original sigue para aquellos que quieren aprender sobre referencias débiles ---

En el algoritmo de marca y barrido, las referencias suaves no se siguen desde el hilo principal (y, por lo tanto, no se marcan a menos que una rama diferente pueda alcanzarla a través de referencias no blandas).

En el algoritmo de copia, las referencias blandas de Objetos no se copian (de nuevo, a menos que se alcancen con una referencia diferente).

Básicamente, cuando se sigue la red de referencias del subproceso de ejecución "principal", no se siguen las referencias blandas. Esto permite que sus objetos se recolecten como si no tuvieran referencias que los señalaran.

Es importante mencionar que las referencias blandas casi nunca se usan de forma aislada. Normalmente se usan en objetos en los que el diseño debe tener múltiples referencias al objeto, pero solo se necesita borrar una referencia para activar la recolección de basura (para facilitar el mantenimiento del contenedor, o el rendimiento en el tiempo de ejecución de no tener que buscar referencias costosas) .