diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCache.java b/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCache.java index 41ec3e2c9..334a0838a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCache.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCache.java @@ -11,6 +11,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Serializable; +import java.lang.ref.SoftReference; import java.util.ArrayList; import java.util.Comparator; import java.util.Iterator; @@ -37,7 +38,7 @@ public class DefaultServerCache implements ServerCache { /** * The underlying map (ConcurrentHashMap or similar) */ - protected final Map map; + protected final Map> map; protected final CountMetric hitCount; protected final CountMetric missCount; @@ -199,7 +200,8 @@ public class DefaultServerCache implements ServerCache { * Get the cache entry - override for query cache to validate dependent tables. */ protected CacheEntry getCacheEntry(Object id) { - return map.get(key(id)); + final SoftReference ref = map.get(key(id)); + return ref != null ? ref.get() : null; } @Override @@ -213,7 +215,7 @@ public class DefaultServerCache implements ServerCache { @Override public void put(Object id, Object value) { Object key = key(id); - map.put(key, new CacheEntry(key, value)); + map.put(key, new SoftReference<>(new CacheEntry(key, value))); putCount.increment(); } @@ -222,8 +224,8 @@ public class DefaultServerCache implements ServerCache { */ @Override public void remove(Object id) { - CacheEntry entry = map.remove(key(id)); - if (entry != null) { + SoftReference entry = map.remove(key(id)); + if (entry != null && entry.get() != null) { removeCount.increment(); } } @@ -266,6 +268,7 @@ public class DefaultServerCache implements ServerCache { long startNanos = System.nanoTime(); long trimmedByIdle = 0; + long trimmedByGC = 0; long trimmedByTTL = 0; long trimmedByLRU = 0; @@ -274,10 +277,14 @@ public class DefaultServerCache implements ServerCache { long idleExpireNano = startNanos - TimeUnit.SECONDS.toNanos(maxIdleSecs); long ttlExpireNano = startNanos - TimeUnit.SECONDS.toNanos(maxSecsToLive); - Iterator it = map.values().iterator(); + Iterator> it = map.values().iterator(); while (it.hasNext()) { - CacheEntry cacheEntry = it.next(); - if (maxIdleSecs > 0 && idleExpireNano > cacheEntry.getLastAccessTime()) { + SoftReference ref = it.next(); + final CacheEntry cacheEntry = ref.get(); + if (cacheEntry == null) { + it.remove(); + trimmedByGC++; + } else if (maxIdleSecs > 0 && idleExpireNano > cacheEntry.getLastAccessTime()) { it.remove(); trimmedByIdle++; @@ -290,27 +297,27 @@ public class DefaultServerCache implements ServerCache { } } - if (trimForMaxSize > 0) { - trimmedByLRU = activeList.size() - maxSize; - if (trimmedByLRU > 0) { - // sort into last access time ascending - activeList.sort(BY_LAST_ACCESS); - int trimSize = getTrimSize(); - for (int i = trimSize; i < activeList.size(); i++) { - // remove if still in the cache - map.remove(activeList.get(i).getKey()); + if (trimForMaxSize > 0 && activeList.size() > maxSize) { + // sort into last access time ascending + activeList.sort(BY_LAST_ACCESS); + int trimSize = getTrimSize(); + for (int i = trimSize; i < activeList.size(); i++) { + // remove if still in the cache + if (map.remove(activeList.get(i).getKey()) != null) { + trimmedByLRU++; } } } evictCount.add(trimmedByIdle); + evictCount.add(trimmedByGC); evictCount.add(trimmedByTTL); evictCount.add(trimmedByLRU); if (logger.isTraceEnabled()) { long exeMicros = TimeUnit.MICROSECONDS.convert(System.nanoTime() - startNanos, TimeUnit.NANOSECONDS); - logger.trace("Executed trim of cache {} in [{}]millis idle[{}] timeToLive[{}] accessTime[{}]" - , name, exeMicros, trimmedByIdle, trimmedByTTL, trimmedByLRU); + logger.trace("Executed trim of cache {} in [{}]millis idle[{}] timeToLive[{}] accessTime[{}] gc[{}]", + name, exeMicros, trimmedByIdle, trimmedByTTL, trimmedByLRU, trimmedByGC); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheConfig.java b/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheConfig.java index 638f237a2..5f8ed1d5d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheConfig.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheConfig.java @@ -4,7 +4,9 @@ import io.ebean.cache.QueryCacheEntryValidate; import io.ebean.cache.ServerCacheConfig; import io.ebean.cache.ServerCacheOptions; import io.ebean.config.CurrentTenantProvider; +import io.ebeaninternal.server.cache.DefaultServerCache.CacheEntry; +import java.lang.ref.SoftReference; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -17,13 +19,13 @@ public class DefaultServerCacheConfig { private int maxSecsToLive; private int trimFrequency; - private Map map; + private Map> map; public DefaultServerCacheConfig(ServerCacheConfig config) { this(config, new ConcurrentHashMap<>()); } - public DefaultServerCacheConfig(ServerCacheConfig config, Map map) { + public DefaultServerCacheConfig(ServerCacheConfig config, Map> map) { this.config = config; this.map = map; @@ -50,7 +52,7 @@ public class DefaultServerCacheConfig { return config.getShortName(); } - public Map getMap() { + public Map> getMap() { return map; } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerQueryCache.java b/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerQueryCache.java index afbdb37f7..4f437eafc 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerQueryCache.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerQueryCache.java @@ -3,6 +3,8 @@ package io.ebeaninternal.server.cache; import io.ebean.cache.QueryCacheEntry; import io.ebean.cache.QueryCacheEntryValidate; +import java.lang.ref.SoftReference; + /** * Server cache for query caching. *

@@ -27,7 +29,8 @@ public class DefaultServerQueryCache extends DefaultServerCache { @Override protected CacheEntry getCacheEntry(Object id) { Object key = key(id); - CacheEntry entry = map.get(key); + final SoftReference ref = map.get(key); + CacheEntry entry = ref != null ? ref.get() : null; if (entry == null) { return null; }