How to use eviction, listeners, and statistics in Guava Cache

The previous shot covered the insert, retrieve, and refresh operations on the cache. In this shot, we will learn about listeners, eviction, and statistics in Guava Cache.

Cache eviction

Caches are limited by the ram size. Therefore, existing keys have to be removed to make space for the new keys to be inserted. This is called cache eviction.

There are different cache eviction strategies.

1. Time-based eviction

The time-based eviction strategy refers to the expiration of keys based on the duration of time passed since insertion or the last access of the key in the cache.

Method Description
CacheBuilder.expireAfterAccess(long, TimeUnit) Evict the entry after the specified amount of time has passed since the last access of the entry.
CacheBuilder.expireAfterWrite(long, TimeUnit) Evict the entry after the specified amount of time has passed since the entry was created or updated.

TimeUnit in Java provides time durations at a given unit of granularity.

2. Size-based eviction

The size of the cache can be restricted with CacheBuilder.maximumSize(). If the cache size reaches the limit, the oldest items will be evicted. This is called size-based eviction.

LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
                .maximumSize(1000)
                .build(new CacheLoader<String, Integer>() {
                    @Override
                    public Integer load(String key) {
                        return key.length();
                    }
                });

In the code above, a cache with maximum size as 1000 is constructed.

3. Explicit eviction of keys

In some cases, keys in a cache have to be removed explicitly. Explicit eviction of keys can be done through the following methods.

Method Description
cache.invalidateAll(); Evict all the entries in the cache.
cache.invalidateAll(Iterable); Evict the list of keys in the cache.
cache.invalidate(Key); Evict the given entry in the cache.

Listeners

Guava Cache provides flexibility to add removal listeners to take action on eviction of keys from the cache.

In the below code, notification.getCause() returns the reason due to which the entry in the cache was evicted. There are five reasons for an entry to be removed. They are as follows:

  1. Explicit: Entry was manually removed by the user.
  2. Replaced: Entry was replaced by the user.
  3. Collected: Entry was removed automatically because its key or value was garbage-collected.
  4. Expired: Entry was removed due to expiry time.
  5. Size: Entry was removed as the cache size reached the limit.

The code below shows how to register a removal listener that prints out the key and the reason why it was removed.

LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
.removalListener(new RemovalListener<String, Integer>() {
@Override
public void onRemoval(RemovalNotification<String, Integer> notification) {
System.out.println("Key - " + notification.getKey() + " removed due to " + notification.getCause());
}
})
.build(new CacheLoader<String, Integer>() {
@Override
public Integer load(String key) {
return key.length();
}
})

Statistics

We can use CacheBuilder.recordStats() during the cache construction to enable tracking statistics for a given cache.

Some of the statistics tracked are as follows:

Method Description
hitCount() Returns the number of times Cache lookup was successful.
missCount() Returns the number of times Cache lookup failed.
evictionCount() Returns the number of times entries were evicted.

Refer to CacheStats for different statistics provided.

The code below shows how to enable statistics on a cache and how to access the different counters available.

LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
.recordStats()
.build(new CacheLoader<String, Integer>() {
@Override
public Integer load(String key) {
return key.length();
}
});
cache.get("hello");
cache.put("hi", 2);
System.out.println(cache.get("hello"));
System.out.println(cache.get("jinfs"));
System.out.println("cache hit rate - " + cache.stats().hitRate());
System.out.println("cache miss rate - " + cache.stats().missRate());