У меня довольно простая конфигурация кеша:
<cache name="MyCache"
maxElementsInMemory="200000"
eternal="false"
timeToIdleSeconds="43200"
timeToLiveSeconds="43200"
overflowToDisk="false"
diskPersistent="false"
memoryStoreEvictionPolicy="LRU"
/>
Я создаю свой кеш следующим образом:
private Ehcache myCache =
CacheManager.getInstance().getEhcache("MyCache");
Я использую свой кэш так:
public MyResponse processRequest(MyRequest request) {
Element element = myCache.get(request);
if (element != null) {
return (MyResponse)element.getValue();
} else {
MyResponse response = remoteService.process(request);
myCache.put(new Element(request, response));
return response;
}
}
Каждые 10000 вызовов метода processRequest () я записываю статистику о своем кеше следующим образом:
logger.debug("Cache name: " + myCache.getName());
logger.debug("Max elements in memory: " + myCache.getMaxElementsInMemory());
logger.debug("Memory store size: " + myCache.getMemoryStoreSize());
logger.debug("Hit count: " + myCache.getHitCount());
logger.debug("Miss count: " + myCache.getMissCountNotFound());
logger.debug("Miss count (because expired): " + myCache.getMissCountExpired());
.. Я вижу хорошее количество обращений, которое говорит мне, что оно работает.
..Однако я вижу, что через пару часов getMemoryStoreSize () начинает превышать getMaxElementsInMemory (). В конце концов, он становится все больше и больше и делает jvm нестабильным, потому что GC начинает делать Full GC без остановок, чтобы восстановить память (и у меня довольно большой набор ограничений). Когда я профилировал кучу, он указал на SpoolingLinkedHashMap LRU, занимающий большую часть пространства.
У меня действительно много запросов, попадающих в этот кеш, и моя теория состоит в том, что алгоритм LRU в ehcache, возможно, не поспевает за удалением элементов, когда он заполнен. Я попробовал политику LFU, и это также вызвало переполнение хранилища памяти maxElements.
Затем я начал смотреть на код ehcache, чтобы посмотреть, смогу ли я доказать свою теорию (внутри LruMemoryStore $ SpoolingLinkedHashMap):
private boolean removeLeastRecentlyUsedElement(Element element) throws CacheException {
//check for expiry and remove before going to the trouble of spooling it
if (element.isExpired()) {
notifyExpiry(element);
return true;
}
if (isFull()) {
evict(element);
return true;
} else {
return false;
}
}
.. отсюда выглядит нормально, затем посмотрел на метод evict ():
protected final void evict(Element element) throws CacheException {
boolean spooled = false;
if (cache.isOverflowToDisk()) {
if (!element.isSerializable()) {
if (LOG.isDebugEnabled()) {
LOG.debug(new StringBuffer("Object with key ").append(element.getObjectKey())
.append(" is not Serializable and cannot be overflowed to disk"));
}
} else {
spoolToDisk(element);
spooled = true;
}
}
if (!spooled) {
cache.getCacheEventNotificationService().notifyElementEvicted(element, false);
}
}
.. похоже, на самом деле он не выселяется (несмотря на название), а скорее полагается на вызывающего, чтобы выселить. Поэтому я посмотрел на реализацию метода put () и не вижу его вызывающего. Я явно что-то здесь упускаю и буду признателен за помощь.
Спасибо!