123

I'm looking for a simple Java in-memory cache that has good concurrency (so LinkedHashMap isn't good enough), and which can be serialized to disk periodically.

One feature I need, but which has proved hard to find, is a way to "peek" at an object. By this I mean retrieve an object from the cache without causing the cache to hold on to the object any longer than it otherwise would have.

Update: An additional requirement I neglected to mention is that I need to be able to modify the cached objects (they contain float arrays) in-place.

Can anyone provide any recommendations?

sanity
  • 35,347
  • 40
  • 135
  • 226
  • 1
    I'm looking for something similar that is "within process" and lighter weight. I want to use it to store some data within an Eclipse plugin in the heap. Ehcache and JCS seem too heavyweight/distributed/J2EE for my taste. – Uri Feb 22 '09 at 20:46
  • 1
    possible duplicate of [Lightweight Java Object cache API](http://stackoverflow.com/questions/230649/lightweight-java-object-cache-api) – Raedwald Jan 30 '14 at 17:48
  • I will recommend Apache Ignite(https://ignite.apache.org/) – B. S. Rawat Aug 22 '18 at 17:33
  • 2
    That this question is closed (6 years after the fact) and it still something people are wondering today, show how SO's moderator system is failing. – dustinevan Mar 30 '19 at 04:08

9 Answers9

71

Since this question was originally asked, Google's Guava library now includes a powerful and flexible cache. I would recommend using this.

IvanRF
  • 7,115
  • 5
  • 47
  • 71
sanity
  • 35,347
  • 40
  • 135
  • 226
  • 5
    Please note that Spring does not support Guava cache anymore: https://stackoverflow.com/questions/44175085/why-did-spring-framework-deprecate-the-use-of-guava-cache – sinner Mar 28 '19 at 10:47
  • @sanity does it implement Jcache? – Gaurav Nov 25 '19 at 06:39
  • 1
    [Caffine](https://github.com/ben-manes/caffeine) is also nice and performant caching library. Caffeine is a high performance, near optimal caching library based on Java 8. Caffeine provides an in-memory cache using a Google Guava inspired API – Slavus Apr 16 '20 at 06:19
38

Ehcache is a pretty good solution for this and has a way to peek (getQuiet() is the method) such that it doesn't update the idle timestamp. Internally, Ehcache is implemented with a set of maps, kind of like ConcurrentHashMap, so it has similar kinds of concurrency benefits.

Alex Miller
  • 69,183
  • 25
  • 122
  • 167
  • 2
    Thanks, an additional question: If I retrieve an object from EHcache (say, an array), and modify it - will the object be updated in the cache? ie. is EHCache maintaining references to the objects? – sanity Feb 22 '09 at 20:38
  • 1
    I believe so but to do this safely you must appropriately lock the object, of course. – Alex Miller Feb 23 '09 at 02:49
  • I love ehcache, however, it is a 10mb jar. Way too big. – markthegrea Oct 22 '19 at 20:10
34

If you're needing something simple, would this fit the bill?

Map<K, V> myCache = Collections.synchronizedMap(new WeakHashMap<K, V>());

It wont save to disk, but you said you wanted simple...

Links:

(As Adam commented, synchronising a map has a performance hit. Not saying the idea doesn't have hairs on it, but would suffice as a quick and dirty solution.)

Evan
  • 18,183
  • 8
  • 41
  • 48
  • 4
    Synchronizing an entire gigantic map is a hefty penalty. You could easily store weak types in a concurrent hash map and remove them periodically. – Adam Gent Feb 23 '11 at 00:11
  • 11
    ConcurrentHashMap is better than Collections.synchronizedMap http://stackoverflow.com/questions/6692008/java-concurrenthashmap-is-better-than-hashmap-performance-wise – Pablo Moretti Jul 11 '12 at 20:17
  • 11
    Performance-wise, ConcurrentHashMap absolutely performs better, but it doesn't share the properties of a WeakHashMap with regard to allowing the garbage collector to reclaim memory. This may or may not be important to you. – Evan Jul 13 '12 at 00:03
  • 7
    Then use `ConcurrentHashMap>`. http://docs.oracle.com/javase/7/docs/api/java/lang/ref/WeakReference.html – jdmichal Sep 22 '16 at 18:23
21

Another option for an in-memory java cache is cache2k. The in-memory performance is superior to EHCache and google guava, see the cache2k benchmarks page.

The usage pattern is similar to other caches. Here is an example:

Cache<String,String> cache = new Cache2kBuilder<String, String>() {}
  .expireAfterWrite(5, TimeUnit.MINUTES)    // expire/refresh after 5 minutes
  .resilienceDuration(30, TimeUnit.SECONDS) // cope with at most 30 seconds
                                          // outage before propagating 
                                          // exceptions
  .refreshAhead(true)                       // keep fresh when expiring
  .loader(new CacheLoader<String, String>() {
    @Override
    public String load(final String key) throws Exception {
      return ....;
    }
  })
  .build();
String val = cache.peek("something");
cache.put("something", "hello");
val = cache.get("something");

If you have google guava as dependency then trying out guava cache, may be a good alternative.

cruftex
  • 5,545
  • 2
  • 20
  • 36
10

Try this:

import java.util.*;

public class SimpleCacheManager {

    private static SimpleCacheManager instance;
    private static Object monitor = new Object();
    private Map<String, Object> cache = Collections.synchronizedMap(new HashMap<String, Object>());

    private SimpleCacheManager() {
    }

    public void put(String cacheKey, Object value) {
        cache.put(cacheKey, value);
    }

    public Object get(String cacheKey) {
        return cache.get(cacheKey);
    }

    public void clear(String cacheKey) {
        cache.put(cacheKey, null);
    }

    public void clear() {
        cache.clear();
    }

    public static SimpleCacheManager getInstance() {
        if (instance == null) {
            synchronized (monitor) {
                if (instance == null) {
                    instance = new SimpleCacheManager();
                }
            }
        }
        return instance;
    }

}
Serhii Bohutskyi
  • 2,261
  • 2
  • 28
  • 28
  • The problems with relying on a synchronized map were already discussed in other answers. – sanity Mar 25 '13 at 16:00
  • @sergeyB : I was looking for a similar solution which is simple for implementing cache... Thanks – Prakruti Pathik Aug 11 '15 at 12:53
  • 1
    `public static SimpleCacheManager getInstance() {` should be `public synchronized static SimpleCacheManager getInstance() {` otherwise `if (instance == null) {` is not thread safe. In which case you can remove the monitor object all together since synchronization takes place for all getInstance() calls, see: http://www.javaworld.com/article/2073352/core-java/simply-singleton.html?page=2 – Can Kavaklıoğlu Dec 15 '16 at 06:48
  • May I recommend using an enum for singleton? https://dzone.com/articles/java-singletons-using-enum – Erk Jun 17 '18 at 21:44
10

You can easily use imcache. A sample code is below.

void example(){
    Cache<Integer,Integer> cache = CacheBuilder.heapCache().
    cacheLoader(new CacheLoader<Integer, Integer>() {
        public Integer load(Integer key) {
            return null;
        }
    }).capacity(10000).build(); 
}
yusufaytas
  • 1,231
  • 13
  • 20
10

Try @Cacheable from jcabi-aspects. With a single annotation you make the entire method result cacheable in memory:

public class Resource {
  @Cacheable(lifetime = 5, unit = TimeUnit.SECONDS)
  public String load(URL url) {
    return url.openConnection().getContent();
  }
}

Also, read this article: http://www.yegor256.com/2014/08/03/cacheable-java-annotation.html

yegor256
  • 102,010
  • 123
  • 446
  • 597
3

How about this: https://commons.apache.org/proper/commons-jcs/ (updated to new address, as JCS is now in Apache Commons)

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
TofuBeer
  • 60,850
  • 18
  • 118
  • 163
1

Try Ehcache? It allows you to plug in your own caching expiry algorithms so you could control your peek functionality.

You can serialize to disk, database, across a cluster etc...

Stephen
  • 19,488
  • 10
  • 62
  • 83