java ehcache lru_ehcache缓存淘汰浅析

最近要设计个缓存系统,所以到处看了看,简单看了ehcache,不得不感叹源码太多了,能写成这样也I服了Y,还是像google的guava看齐吧,当然ehcache号称支持分布式

1简单使用

记录下简单使用,主要是看源码的时候可以找到入口

http://passover.blog.51cto.com/2431658/486709

maxElementsInMemory="10000"

eternal="false"

timeToIdleSeconds="120"

timeToLiveSeconds="120"

overflowToDisk="true"

diskPersistent="false"

diskExpiryThreadIntervalSeconds="120"

memoryStoreEvictionPolicy="LRU"

/>

maxElementsInMemory="5"   //缓存中允许创建的最大对象数

maxElementsOnDisk = "5"

eternal="false"//缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期。

timeToIdleSeconds="1440"   //缓存数据的钝化时间,也就是在一个元素消亡之前,两次访问时间的最大时间间隔值

diskPersistent="false"

timeToLiveSeconds="2880"   //缓存数据的生存时间,也就是一个元素从构建到消亡的最大时间间隔值

overflowToDisk="false"//内存不足时,是否启用磁盘缓存

memoryStoreEvictionPolicy="FIFO"//缓存满了之后的淘汰算法

statistics="true"

/>

public class EhcacheTest {

private static final Logger logger = Logger.getLogger(EhcacheTest.class);

private static Cache sampleCache = null;

public static void main(String[]args) {

init();

test();

}

private static void test() {

logger.info(sampleCache.getMemoryStoreEvictionPolicy());

for(inti=0; i<10; i++){

//写入缓存

sampleCache.put(new Element(i, "v" + i));

//打印当前缓存的所有值

logger.info(sampleCache.getKeys());

//读取缓存

Element e = sampleCache.get(i);

logger.info(e.getValue());

}

//打印命中统计

logger.info(sampleCache.getStatistics());

}

private static voidinit() {

CacheManager manager = CacheManager.create();

//      manager.addCache("sample"); //已经在配置文件定义过了

sampleCache = manager.getCache("sample");

}

}

另一种使用方式:

String name = "Namespace";

intcapacity = 500;

intrefreshPeriod = 5000;

// Initialize EhCache

Cache cache = new Cache(name, capacity, false, false, refreshPeriod, 0);

cache.initialise();

System.out.println(

"Initialize EhCache: " +

"   name:       " + name +

"   capacity:   " + capacity +

"   expire:     " + refreshPeriod

);

// Set data into EhCache

String key1 = "Key";

String value1 = "Value";

Element element1 = new Element(key1, value1);

cache.put(element1);

System.out.println("Set (" + key1 + ", " + value1 + ") into EhCache.");

// Get data from EhCache

Element element2 = cache.get(key1);

String key2 = (String) element2.getObjectKey();

String value2 = (String) element2.getObjectValue();

System.out.println("Get (" + key2 + ", " + value2 + ") from EhCache.");

// Remove data from EhCache

if (cache.remove(key2)) {

System.out.println("Remove data with key = " + key2 + " successfully.");

}

// Get EhCache size

System.out.println("EhCache size is " + cache.getSize());

最重要的入口就是CacheManager和Cache,Manager起到类似工厂的作用,主要还是看Cache

2CacheManager

CacheManager,默认会去找ehcache.xml以及ehcache-failsafe.xml等文件,最后创建的是一个Cache对象

private void addConfiguredCaches(ConfigurationHelper configurationHelper) {

// 根据配置创建缓存Cache对象

Set unitialisedCaches = configurationHelper.createCaches();

for (Iterator iterator = unitialisedCaches.iterator();

iterator.hasNext();) {

EhcacheunitialisedCache = (Ehcache) iterator.next();

//调用Cache的init方法

addCacheNoCheck(unitialisedCache, true);

......

}

}

}

============================================================

//在Cache的init方法中,会根据不同的淘汰策略,选择不同的Store

if (useClassicLru &&

configuration.getMemoryStoreEvictionPolicy().equals(

MemoryStoreEvictionPolicy.LRU)) {

//老的LRU策略,选择LruMemoryStore

Store disk = createDiskStore();

store = new LegacyStoreWrapper(new LruMemoryStore(this, disk), disk,registeredEventListeners, configuration);

} else {

//可以存到磁盘,DiskBackedMemoryStore(Memory和DiskStore的整合)

//新的淘汰机制,貌似会有些问题

if (configuration.isOverflowToDisk()) {

store = DiskBackedMemoryStore.create(this, onHeapPool, onDiskPool);

} else {

//只在内存上,MemoryOnlyStore

store = MemoryOnlyStore.create(this, onHeapPool);

}

}

store可以有很多实现,包括DiskBackedMemory(使用DiskStore)或者MemoryOnlyStore(使用MemoryStore)或者

LocalTransactionStore/JtaLocalTransactionStore之类的,分布式的靠Listener去实现,cache的put、update、remove、evict等操作都会发出事件,然后由listener去实现,比如RMISynchronousCacheReplicator

这些store的实现都和ConcurrentHashMap类似,也是分段,默认分64个段即64个锁

3淘汰机制

LFU最不经常使用,计算频率,也就是get的次数

FIFO先进先出,计算下写入时间

LRU最近最少使用,有些使用链表,有些只是记录一个最后访问时间

是否要淘汰都是根据最大容量,超过最大容量后,就会根据指定的策略淘汰数据,先看看新的淘汰算法吧

3.1新的Store

主要看看MemoryStore,在Store内部,有一个SelectableConcurrentHashMap,和ConcurrentHashMap很像,也是采用分段机制,但是他的淘汰机制好像非常不好,没有使用链表的形式,而是在所有元素中遍历,

//先找到要淘汰的个数

intevict = Math.min(map.quickSize() - maximumSize, MAX_EVICTION_RATIO);

for (inti = 0; i 

//每次随机选出几个,再确定最应该淘汰的一个,效率不高removeElementChosenByEvictionPolicy(elementJustAdded);

}

public Element[] getRandomValues(finalintsize, Object keyHint) {

ArrayList sampled = new ArrayList(size * 2);

// pick a random starting point in the map

intrandomHash = rndm.nextInt();

finalintsegmentStart;

if (keyHint == null) {

segmentStart = (randomHash >>> segmentShift) & segmentMask;

} else {

segmentStart = (hash(keyHint.hashCode()) >>> segmentShift) & segmentMask;

}

intsegmentIndex = segmentStart;

do {

final HashEntry[] table = segments[segmentIndex].table;

finalinttableStart = randomHash & (table.length - 1);

inttableIndex = tableStart;

do {

for (HashEntry e = table[tableIndex]; e != null; e = e.next) {

Element value = e.value;

if (value != null && (!(e.pinned && elementPinningEnabled) || value.isExpired())) {

sampled.add(value);

}

}

if (sampled.size() >= size) {

return sampled.toArray(new Element[sampled.size()]);

}

//move to next table slot

tableIndex = (tableIndex + 1) & (table.length - 1);

} while (tableIndex != tableStart);

//move to next segment

segmentIndex = (segmentIndex + 1) & segmentMask;

} while (segmentIndex != segmentStart);

return sampled.toArray(new Element[sampled.size()]);

}

整了半天,貌似是为了随机定位到某个段,然后随机从某个元素开始遍历,直到找到指定个数的需要淘汰的数量,没仔细去想并发问题了,觉得效率比较低,不管了,还是

着重看下他的淘汰策略吧:

public Element selectedBasedOnPolicy(Element[] sampledElements, Element justAdded) {

//edge condition when Memory Store configured to size 0

if (sampledElements.length == 1) {

return sampledElements[0];

}

Element lowestElement = null;

for (Element element : sampledElements) {

if (element == null) {

continue;

}

if (lowestElement == null) {

if (!element.equals(justAdded)) {

lowestElement = element;

}

} else if (

compare(lowestElement,

element) && !element.equals(justAdded)) {

lowestElement = element;

}

}

return lowestElement;

}

LRU LFU FIFO的核心就在对compare的实现,

LRU:比较最后访问时间

public boolean compare(Element element1, Element element2) {

return element2.getLastAccessTime() 

}

LFU:比较get次数(在get的时候会累加)

public boolean compare(Element element1, Element element2) {

return element2.getHitCount() 

}

FIFO:创建/修改时间

public boolean compare(Element element1, Element element2) {

return element2.getLatestOfCreationAndUpdateTime() <

element1.getLatestOfCreationAndUpdateTime();

}

3.2老的LruMemoryStore

新的那种随机定位的方式让人感觉很怪,老的LRU实际使用的是SpoolingLinkedHashMap,

他在LinkedHashMap上增加了,他的put/get/remove是synchronized的,,所以没有并发安全性问题,但是感觉效率也不太高。。就是LinkedHashMap的同步版本了

4总结

没有发现啥亮点,代码太多了,看的几部分也没啥参考价值。。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值