¿Por qué la memoria caché de Google en Java siempre llama a cacheloader?
Código Java
LoadingCache lt key, gráfico gtgraphs =CacheBuilder.newBuilder()
. Tamaño máximo (1000)
. expireAfterWrite(10, unidad de tiempo. Minutos)
. eliminar OYENTE(MI _ OYENTE)
. build(
newCacheLoader lt key, graph gt(){
publicGraph load(Key key) genera cualquier excepción{
return createExpensiveGraph(key);< / p>
}
});
Adaptable
El almacenamiento en caché puede resultar muy útil en muchas situaciones. Por ejemplo, necesitamos obtener valores varias veces en función de una entrada determinada, y el costo de calcular u obtener valores es muy elevado.
Cache y ConcurrentMap son muy similares, pero no exactamente iguales. La diferencia más fundamental es que ConcurrentMap guardará todos los objetos agregados hasta que se elimine el objeto mostrado. Para limitar el uso de memoria, las cachés suelen configurarse para eliminar objetos automáticamente. Es útil en algunos casos, como LoadingCache, que carga automáticamente objetos almacenados en caché incluso si el objeto no se ha eliminado automáticamente.
En términos generales, el caché de Guava es adecuado para las siguientes situaciones:
Estás dispuesto a gastar algo de memoria a cambio de mejorar el rendimiento;
Predices que algunos las claves serán consultadas muchas veces;
Sus datos almacenados en caché no excederán la memoria (el caché de Guava es un caché local dentro de una sola aplicación. No almacena datos en archivos o servidores externos. Si eso no funciona Para usted, considere Memcached.
Si sus necesidades cumplen con los requisitos anteriores, entonces es absolutamente correcto elegir Guava cache.
Puede obtener el caché utilizando el modo de compilación de CacheBuilder. como se muestra en el ejemplo anterior, pero cómo personalizarlo es más interesante.
Nota: si no necesita estas funciones de caché, usar ConcurrentHashMap tendrá una mejor eficiencia de la memoria, pero puede ser muy difícil. o imposible copiar algunas características de Cache basadas en el antiguo ConcurrentMap Posiblemente
Cargando
Lo primero que hay que aclarar sobre el almacenamiento en caché es: ¿hay alguna forma de calcular/cargar el correspondiente? valor por una clave determinada? Si es así, ¿se puede hacer usando CacheLoader? No existe tal método, o si desea anular el método de carga de caché pero aún desea conservar la semántica de "get-if-abstract-compute", puede pasar una instancia invocable al llamar al método get para lograr el propósito. El objeto almacenado en caché se puede insertar directamente a través de Cache.put, pero se prefiere la carga automática, porque la carga automática puede determinar más fácilmente la coherencia de toda la información del caché. /p>
Desde el cargador de caché
El caché LoadingCache se construye a través de CacheLoader. Para crear un CacheLoader, simplemente implemente el método de excepción de lanzamiento de carga V. El siguiente ejemplo. cómo crear un LoadingCache:
LoadingCache lt key, gráfico gtgraphs =CacheBuilder.newBuilder()
Tamaño máximo (1000)
.
build(
newCacheLoader lt key, graph gt(){
publicGraph load(Key key) genera cualquier excepción{
return createExpensiveGraph(key);< / p>
}
});
...
Pruebe {
return graphs.get(key) ;
}catch(ExecutionException e){
thrownewOtherException(e . get causa());
}
get(K ) El método se puede utilizar para consultar LoadingCache. Este método devuelve el valor almacenado en caché o carga automáticamente el valor correspondiente en el caché a través de CacheLoader. Lo que hay que tener en cuenta aquí es que CacheLoader puede generar una excepción y LoaderCache.get (K) puede generar ExecutionException. Si el CacheLoader definido no declara una excepción de verificación, puede obtener el valor de la caché llamando a getUnchecked(K); sin embargo, una vez que se declara una excepción de verificación en el CacheLoader, no puede llamar a getUnchecked;
Código Java
Tecla LoadingCache lt, gráfico gtgraphs =CacheBuilder.newBuilder()
. expireAfterAccess(10, unidad de tiempo. Minutos)
. Build(
newCacheLoader lt key, graph gt(){
publicGraph load(Key key){//No se detectó ninguna excepción
Devuelve createExpensiveGraph(key);
}
});
...
los gráficos de retorno se desmarcan (clave);
< p. >Las consultas por lotes pueden usar getAll(IterableltCabe señalar que la implementación de CacheLoader.loadAll puede cargar valores almacenados en caché para claves que no se requieren explícitamente. Por ejemplo, al calcular algunas claves en un grupo , loadAll El método también puede cargar los valores de las claves restantes en el grupo
De invocables
Todos los cachés de Guava admiten el método get(K, Callable(V)), independientemente de si lo son. Cuando el valor de caché para la clave dada ya existe, se devuelve directamente, de lo contrario, se calculará y almacenará en el caché mediante el método invocable especificado y el caché correspondiente no se cambiará; hasta que se complete la carga "Si está en caché, devuelva; de lo contrario, cree, almacene en caché y devuelva" semántica
Código Java
Clave lt de caché, valor gtcache =CacheBuilder.newBuilder()
. Tamaño máximo (1000)
. build(); //Mira, no hay CacheLoader
...
Prueba {
//Si la clave no está en el grupo "fácil de calcular", necesitamos
//hacer las cosas de la manera más difícil.
cache.get(key, newCallable lt value gt(){
@Override
publicValue call() generó alguna excepción{
return doThingsTheHardWay(key);
}
});
}catch(ExecutionException e){
thrownewOtherException(e . get cause());
}
Inserción directa
Utilice el método cache.put(key, value) para insertar el valor directamente en el caché, pero esto sobrescribirá el valor existente en el caché. El caché también se puede modificar utilizando métodos en el objeto ConcurrentMap exportado por Cache.asMap(). Sin embargo, tenga en cuenta que asMap no tiene forma de cargar datos automáticamente en la memoria caché. En otras palabras, los métodos en asMap operan fuera del alcance de carga automática del caché. Por lo tanto, cuando use CacheLoader o Callable para cargar el caché, debe darle prioridad a Cache.get(K, Callablelt
Cache Recycling
La cruel realidad es que podemos decir con certeza que no tenemos suficiente memoria para almacenar en caché todo. Debe decidir: ¿cuándo ya no vale la pena conservar el valor almacenado en caché? Guava proporciona tres estrategias básicas de desalojo de caché: estrategia de desalojo basada en capacidad, estrategia de desalojo basada en tiempo y desalojo basado en referencias. estrategia.
Estrategia de recuperación basada en capacidad
Utilice CacheBuilder.maximumSize(long) para establecer el tamaño máximo del caché. El caché intentará reciclar los elementos del caché que no se han utilizado. Recientemente o se utilizan con poca frecuencia. Advertencia: el caché se puede reciclar antes de que se alcance la capacidad, generalmente cuando el tamaño del caché está cerca del tamaño límite.
Además, si los diferentes elementos del caché tienen diferentes "pesos", como elementos de caché que tienen diferentes ocupaciones de memoria. Debe especificar una función de cálculo de peso con CacheBuilder.weigher(Weigher) y establecer el peso total con maxmumhweight(long). también se reciclarán. Los pesos se calculan en el momento de la creación y no se modifican posteriormente.
Código Java
LoadingCache lt clave, gráfico gtgraphs =CacheBuilder.newBuilder()
. Peso máximo (100000)
.Weigher(
newWeigher lt clave, gráfico gt(){
Pesaje público (clave k, gráfico g) {
Devuelve g.vertices();
}
})
nueva clave CacheLoader lt, gráfico gt(. ){
carga de publicGraph(clave clave){//No se detectó ninguna excepción
Devuelve createExpensiveGraph(clave);
}
} );
Estrategia de recuperación basada en el tiempo
CacheBuilder proporciona dos métodos de recuperación basados en el tiempo:
Cuando un elemento de caché no se ha leído ni escrito durante un período específico período de tiempo, caducará después del acceso (entero largo, unidad de tiempo).
Esta estrategia de reciclaje es similar a la estrategia de reciclaje basada en capacidad;
Cuando el elemento de la caché no se actualiza dentro del período de tiempo especificado, se reciclará ExpireAfterWrite(long, TimeUnit). Esta estrategia se puede utilizar si creemos que los datos almacenados en caché ya no estarán disponibles después de un tiempo.
Como se describe a continuación, la recopilación de vencimientos programados se realizará periódicamente durante las escrituras y ocasionalmente durante las lecturas.
Probar la recuperación programada
Probar la recuperación programada no tiene por qué ser tan doloroso. No necesitamos dedicar 2 segundos a probar un tiempo de caducidad de 2 segundos. Al crear el caché, usamos la interfaz Ticker y especificamos la fuente de tiempo a través del método CacheBuilder.ticker (Ticker), para no tener que esperar a que el reloj del sistema disminuya.
Estrategia de desalojo basada en referencias
Guava puede configurar el caché para permitir la recolección de basura a través de referencias débiles a claves o valores de caché, o referencias suaves a valores de caché.
CacheBuilder.weakKeys() utiliza referencias débiles para almacenar claves. Cuando no hay ninguna referencia (fuerte o suave) a la clave, la entrada de caché correspondiente se recolectará como basura. Debido a que la recolección de basura se basa en == para juzgar, esto hará que todo el caché use == para comparar la igualdad de claves en lugar de usar equals();
CacheBuilder.weakValues() usa referencias débiles para almacenar Valor de caché. Cuando no hay referencias (fuertes o suaves) a un elemento de caché, se recolectará como basura. Debido a que la recolección de basura se basa en == para juzgar, esto hará que todo el caché también use == para comparar la igualdad de los valores del caché, en lugar de usar equals();
CacheBuilder.softValues() usa soft referencias a Almacena el valor almacenado en caché. Cuando se necesita una respuesta, las referencias suaves se recuperan mediante la recolección de basura utilizando el principio menos utilizado. Debido a que el uso de referencias suaves puede afectar el rendimiento, recomendamos encarecidamente utilizar una política de tamaño máximo de caché predecible. La comparación de valores almacenados en caché usando softValues () también usa == en lugar de igual ().
Eliminación de monitores
En cualquier momento, en lugar de esperar pasivamente a que se recicle, puede eliminar explícitamente el caché no válido mediante el siguiente método:
Utilice caché .invalidate(key) lo elimina individualmente;
Usa el almacenamiento en caché. (Claves) no válidas para eliminar en lotes;
Utilice Cache.invalidateAll() para eliminar todo.