I have created a Guava CacheBuilder based cache with on expiry of 5 seconds if key is not written to. Have added a removalListener to it which prints the key/value pair being removed. What I have observed is that the onRemoval method of the listener gets called only the first time. It doesn't get called the second time an entry is removed. (The actual removal happens. Just the onRemoval method of the removalListener doesn't get called).
Am I doing something wrong? Can somebody help? Thanks in advance. Here's my code:
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;
public class TestCacheBuilder {
public static void main(String[] args) {
try {
new TestCacheBuilder();
}catch (Exception e){
e.printStackTrace();
}
}
public TestCacheBuilder() {
Cache<String, String> myCache = CacheBuilder.newBuilder()
.expireAfterWrite(5, TimeUnit.SECONDS)
.removalListener(new RemovalListener<String, String>() {
public void onRemoval(RemovalNotification<String, String> removal) {
System.out.println("removal: "+removal.getKey()+"/"+removal.getValue());
}
})
.build();
Map<String, String> inMap = myCache.asMap();
inMap.put("MyKey", "FirstValue");
System.out.println("Initial Insert: "+inMap);
//Wait 16 seconds
try {
Thread.sleep(4000);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
System.out.println("After 4 seconds: " + inMap);
inMap.put("MyKey", "SecondValue");
try {
Thread.sleep(1000);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
System.out.println("After 1 more second: " + inMap);
try {
Thread.sleep(4000);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
System.out.println("After 4 more seconds: " + inMap);
}
}
The output is as below:
Initial Insert: {MyKey=FirstValue}
After 4 seconds: {MyKey=FirstValue}
removal: MyKey/FirstValue
After 1 more second: {MyKey=SecondValue}
After 4 more seconds: {}
README.md. A Guava cache extension that allows caches to persist cache entries when they cannot longer be stored in memory. An implementation that overflows to the file system is provided including a corresponding CacheBuilder with similar semantics than the Guava CacheBuilder .
Guava provides a very powerful memory based caching mechanism by an interface LoadingCache<K,V>. Values are automatically loaded in the cache and it provides many utility methods useful for caching needs.
Guava Caches store values in RAM.
A semi-persistent mapping from keys to values. Values are automatically loaded by the cache, and are stored in the cache until either evicted or manually invalidated. Implementations of this interface are expected to be thread-safe, and can be safely accessed by multiple concurrent threads.
The removal doesn't actually happen directly: Guava does not have its own cleaning thread to remove expired entries. The removals happen usually when there's a write in the same segment of the cache, or during a read when a certain amount of time has passed (to amortize the cost of the removal). However, while the entry is still there, it's seen as expired, which is why it's not printed.
Quoting CacheBuilder's javadoc:
If expireAfterWrite or expireAfterAccess is requested entries may be evicted on each cache modification, on occasional cache accesses, or on calls to Cache.cleanUp(). Expired entries may be counted in Cache.size(), but will never be visible to read or write operations.
As an alternative to Guava Cache
you can use Caffeine, "a high performance, near optimal caching library based on Java 8".
It has "Google Guava inspired API" and performs eviction periodically, so it may fit your use case better (if you're worried about memory not being freed semi-automatically). See Eviction wiki page:
Expiration is performed with periodic maintenance during writes and occasionally during reads. Scheduling and firing of an expiration event is executed in amortized O(1) time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With