2010-01-28 17 views
24

Necesito almacenar en caché los objetos en Java usando una proporción de la memoria RAM disponible. Soy consciente de que otros han hecho esta pregunta, pero ninguna de las respuestas cumple mis requisitos.¿Cómo guardo los objetos en Java de forma eficiente utilizando la memoria RAM disponible?

Mis requisitos son:

  • simple y ligero
  • No es dramáticamente más lento que un HashMap llanura
  • Uso LRU, o alguna política de eliminación que se aproxima LRU

Probé LinkedHashMap, sin embargo, requiere que especifique una cantidad máxima de elementos, y no sé cuántos elementos se necesitarán para llenar la RAM disponible (sus tamaños variarán significativamente).

Mi enfoque actual es el uso de Map Maker de Google Colección de la siguiente manera:

Map<String, Object> cache = new MapMaker().softKeys().makeMap(); 

Esto parecía atractivo, ya que se debe eliminar automáticamente los elementos cuando se necesita más memoria RAM, sin embargo hay un grave problema: su comportamiento es llenar toda la memoria RAM disponible, momento en el que el GC comienza a temblar y el rendimiento de la aplicación se deteriora drásticamente.

He oído hablar de cosas como EHCache, pero parece bastante pesado para lo que necesito, y no estoy seguro de si es lo suficientemente rápido para mi aplicación (recordando que la solución no puede ser mucho más lenta) que un HashMap).

+0

¿qué tipo de objetos está almacenando en caché? No estoy del todo claro por qué le preocupa el rendimiento de la memoria caché, ya que tan pronto como esté tras una política de caducidad, va a incurrir en más sobrecarga que un mapa simple y EHCache es una lib de caché bien desarrollada, que (Estoy pensando en configurarlo a través de Spring aquí) no es complejo de configurar y tan fácil de usar que un mapa. – beny23

+0

Los objetos varían en tamaño desde aproximadamente 1 kb hasta quizás 10 kbs. Me preocupa el rendimiento porque recuperar objetos de la memoria caché se encuentra en el ciclo interno de un proceso muy intensivo de la CPU. Si es lento, puede aumentar el tiempo requerido para que mi aplicación haga su trabajo de minutos a horas. – sanity

+0

Con softKeys() no obtendrá un golpe si usa igual(), solo obtendrá un golpe si está buscando el objeto con igualdad de referencia. Si necesita equals() para hits de caché, use softValues ​​() en su lugar. –

Respuesta

7

Tengo requisitos similares a usted - concurrencia (en 2 CPUs hexacore) y LRU o similar - y también intentó Guava MapMaker. Encontré SoftValues ​​() mucho más lento que weakValues ​​(), pero ambos hicieron que mi aplicación fuera mucho más lenta cuando la memoria se llenó.

Intenté WeakHashMap y fue menos problemático, extrañamente incluso más rápido que usar LinkedHashMap como un caché LRU a través de su método removeEldestEntry().

Pero el más rápido para mí es ConcurrentLinkedHashMap, lo que ha hecho mi aplicación 3-4 (!!) veces más rápido que cualquier otra memoria caché que probé. ¡Alegría, después de días de frustración! Aparentemente se ha incorporado al MapMaker de Guava, pero la función LRU no está en Guava r07 en cualquier caso. Espero que funcione para ti.

0

esto parecía atractivo, ya que debe eliminar automáticamente los elementos cuando se necesita más memoria RAM, sin embargo hay un problema serio : su comportamiento es llenar toda la memoria RAM disponible

Utilizando las teclas programables solo permite que el recolector de basura elimine objetos de la memoria caché cuando ningún otro objeto los referencia (es decir, cuando lo único que se refiere a la clave de la memoria caché es la memoria caché). No garantiza ningún otro tipo de expulsión.

La mayoría de las soluciones que encontrará serán características agregadas sobre las clases de mapas de Java, incluido EhCache.

¿Has mirado el commons-collections LRUMap?

Tenga en cuenta que existe un open issue contra MapMaker para proporcionar funcionalidad LRU/MRU. Quizás también pueda expresar su opinión allí

+0

LRUMap requiere que especifique el tamaño máximo del mapa, pero no sé lo que será, solo quiero que la memoria caché siga creciendo para llenar la memoria RAM que no se utiliza. – sanity

0

Usando su memoria caché existente, almacene WeakReference en lugar de refererencias de objeto normales.

Si el GC comienza a quedarse sin espacio libre, se liberarán los valores mantenidos por WeakReferences.

+0

¿Eso se aproximaría a LRU? – beny23

+0

Está pensando en 'SoftReference': los objetos débilmente referenciados pueden recuperarse en cualquier momento, sin importar la memoria libre. – danben

+0

Consulte esta charla sobre las diferencias entre las referencias débiles y suaves: http://crazybobs-talks.googlecode.com/svn/trunk/out/references.pdf y http://www.parleys.com/#st=5&id= 2657 y sl = 53. –

-3

Suponiendo que desea que la memoria caché sea segura para subprocesos, entonces debe examinar el ejemplo de caché en el libro de Brian Goetz "Concurrencia de Java en la práctica". No puedo recomendar esto lo suficiente.

+0

Eso realmente no resolvió su pregunta para nada. – danben

+0

@danben: Estoy de acuerdo. Pero la seguridad de las hebras es algo que tendrá que considerar. –

4

Implementé las memorias caché de serval y es probablemente tan difícil como implementar un nuevo origen de datos o subprocesos, mi recomendación es usar jboss-cache u otra conocida lib de caché. Así que dormirá bien sin problemas

0

En el pasado he usado JCS. Puede configurar el configuration para tratar de satisfacer sus necesidades. No estoy seguro de si esto satisfará todos sus requisitos/necesidades, pero me pareció bastante poderoso cuando lo usé.

0

No se puede "eliminar elementos" sólo se puede dejar de hacer referencia a ellos con fuerza y ​​esperar a que el GC para limpiarlos, para seguir con Google Colecciones ...

0

No estoy al tanto de una manera fácil averiguar el tamaño de un objeto en Java. Por lo tanto, no creo que encuentre una forma de limitar una estructura de datos por la cantidad de RAM que está tomando.

En función de esta suposición, tiene que limitarse a la cantidad de objetos en caché. Sugeriría ejecutar simulaciones de algunos escenarios de uso de la vida real y recopilar estadísticas sobre los tipos de objetos que entran en el caché. Luego puede calcular el tamaño estadísticamente promedio y la cantidad de objetos que puede permitirse almacenar en caché. A pesar de que es solo una aproximación de la cantidad de RAM que desea dedicar a la memoria caché, podría ser suficiente.

En cuanto a la implementación de la memoria caché, en mi proyecto (una aplicación de rendimiento crítico) estamos utilizando EhCache, y personalmente no me parece que sea pesado.

En cualquier caso, ejecute varias pruebas con varias configuraciones diferentes (en cuanto a tamaño, política de desalojo, etc.) y descubra qué es lo mejor para usted.

3

He oído hablar de cosas como EHCache, pero parece bastante pesado para lo que necesito, y no estoy seguro de si es lo suficientemente rápido para mi aplicación (recordando que la solución no puede ser dramáticamente más lento que un HashMap).

Realmente no sé si se puede decir que EHCache es muy pesado. Al menos, no considero EHCache como tal, especialmente cuando se utiliza un Memory Store (que está respaldado por un LinkedHashMap extendido y es, por supuesto, la opción de almacenamiento en caché más rápida). Usted debe darle una oportunidad.

+0

Esta solución comparte el mismo problema que mencioné con LinkedHashMap, concretamente (de los documentos de EHCache) "Todas las memorias caché especifican su tamaño máximo en memoria, en términos de cantidad de elementos, en el momento de la configuración". – sanity

+0

@sanity Ya veo ... pero si no quieres llenar * toda * la memoria disponible con tus objetos en caché (y así evitar el GC), entonces no veo cómo hacer eso sin limitar el número de objetos en el caché ¿Hizo algunos puntos de referencia representativos para tener una idea del tamaño en la memoria para diferentes límites? –

+0

Probablemente terminaré comparándolo si no se presenta una mejor opción, pero me preocupa porque la realidad puede diferir de mis puntos de referencia en formas inesperadas que causan todo tipo de dolores de cabeza. – sanity

1

No sé si esta sería una solución simple, especialmente si se compara con EHCache o similar, pero ¿has mirado el Javolution library? No está diseñado como tal, pero en el paquete javolution.context tienen un patrón de Allocator que puede reutilizar objetos sin la necesidad de recolección de basura. De esta forma, mantienen la creación de objetos y la recolección de basura al mínimo, una característica importante para la programación en tiempo real. Tal vez deberías echarle un vistazo e intentar adaptarlo a tu problema.

2

Creo que MapMaker va a ser la única manera razonable de obtener lo que está pidiendo. Si "el GC comienza a agitarse y el rendimiento de la aplicación se deteriora drásticamente", debe dedicar un tiempo a configurar correctamente los distintos parámetros de ajuste. Este documento puede parecer un poco intimidante al principio, pero en realidad está escrito con mucha claridad y es una mina de oro de información útil acerca de GC:

http://java.sun.com/j2se/reference/whitepapers/memorymanagement_whitepaper.pdf

+0

Estoy familiarizado con ese documento, pero no conozco nada que resuelva mi problema :-( – sanity

0

En caché algo, SoftReference tal vez la mejor manera hasta ahora que puedo imaginar.

O puede reinventar un grupo de objetos. Que cada objeto que no usas, no necesitas destruirlo. Pero para ahorrar CPU en lugar de guardar memoria

Cuestiones relacionadas