ehcache不尊重maxElementsInMemory

时间:2011-12-21 19:52:22

标签: memory ehcache

我有一个相当简单的缓存配置:

 <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;
    }
}


每10,000次调用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 GCs以回收内存(并且我有一个相当大的上限集)。当我分析堆时,它指向LRU的SpoolingLinkedHashMap占用了大部分空间。

我确实有很多请求命中这个缓存,而我的理论是ehcache的LRU算法可能跟不上在它满了时驱逐元素。我尝试了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()方法的实现,我没有看到它调用它。我在这里明显遗漏了一些东西,并希望得到一些帮助。

谢谢!

2 个答案:

答案 0 :(得分:1)

您的配置对我来说还不错。只需使用右键即可进行缓存。

请勿将完整的请求对象用作缓存的键。从您的请求对象中放入一些唯一值。例如:

MyResponse response = remoteService.process(request); 
myCache.put(new Element(request.getCustomerID(), response));
return response;

这应该为您工作。缓存不起作用的原因是每次请求对象都是新对象时;它永远不会从缓存中找到响应,因此会不断添加到缓存中。

答案 1 :(得分:0)

maxElementsInMemory属性已过时,请改用maxEntriesLocalHeap