ehcache3-源码简析三

ehcache3的evict策略是怎样的呢?从put操作可以一窥,这里以单层heap cache为例。

ehcache3的evict策略不可设置,只能通过eviction-advisor建议evict,但这种建议得不到保证且低效。ehcache3的evict策略其实是一种基于样本的LRU算法,即在全量数据中采集一定数量样本(默认为8),在样本集中选取lastAccessTime最小的进行evict。

 1 //put操作先存入元素,然后判断是否进行evict,有删减
 2 public PutStatus put(final K key, final V value) throws StoreAccessException {
 3 
 4   checkKey(key);
 5   checkValue(value);
 6   final long now = timeSource.getTimeMillis();
 7 
 8     //map.compute会进入ConcurrentHashMap遍历元素并计算value
 9   map.compute(key, new BiFunction<K, OnHeapValueHolder<V>, OnHeapValueHolder<V>>() {
10     @Override
11     public OnHeapValueHolder<V> apply(K mappedKey, OnHeapValueHolder<V> mappedValue) {
12 
13       if (mappedValue != null && mappedValue.isExpired(now, TimeUnit.MILLISECONDS)) {
14         updateUsageInBytesIfRequired(- mappedValue.size());
15         mappedValue = null;
16       }
17 
18       if (mappedValue == null) {
19         OnHeapValueHolder<V> newValue = newCreateValueHolder(key, value, now, eventSink);
20         if (newValue != null) {
21           updateUsageInBytesIfRequired(newValue.size());
22           statOutcome.set(StoreOperationOutcomes.PutOutcome.PUT);
23         }
24         return newValue;
25       } else {
26         OnHeapValueHolder<V> newValue = newUpdateValueHolder(key, mappedValue, value, now, eventSink);
27         if (newValue != null) {
28           updateUsageInBytesIfRequired(newValue.size() - mappedValue.size());
29         } else {
30           updateUsageInBytesIfRequired(- mappedValue.size());
31         }
32         statOutcome.set(StoreOperationOutcomes.PutOutcome.REPLACED);
33         return newValue;
34       }
35     }
36   });
37   
38     //enforceCapacity会判断是否进行evict操作。
39   enforceCapacity();
40 
41 }

ConcurrentHashMap的compute操作

  1 public V compute(K key,
  2                  BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
  3   if (key == null || remappingFunction == null)
  4       throw new NullPointerException();
  5   int h = spread(key.hashCode());
  6   V val = null;
  7   int delta = 0;
  8   int binCount = 0;
  9   for (Node<K,V>[] tab = table;;) {
 10     Node<K,V> f; int n, i, fh;
 11     if (tab == null || (n = tab.length) == 0)
 12         tab = initTable();
 13     else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
 14       Node<K,V> r = new ReservationNode<K,V>();
 15       synchronized (r) {
 16         if (casTabAt(tab, i, null, r)) {
 17           binCount = 1;
 18           Node<K,V> node = null;
 19           try {
 20                 //桶内无元素,计算key的value,如果value非null则添加该key-value(Node);
 21                 //如果value为null则什么也不做
 22             if ((val = remappingFunction.apply(key, null)) != null) {
 23                 delta = 1;
 24                 node = new Node<K,V>(h, key, val, null);
 25             }
 26           } finally {
 27             setTabAt(tab, i, node);
 28           }
 29         }
 30       }
 31       if (binCount != 0)
 32         break;
 33     }
 34     else if ((fh = f.hash) == MOVED)
 35       tab = helpTransfer(tab, f);
 36     else {
 37       synchronized (f) {
 38         if (tabAt(tab, i) == f) {
 39           if (fh >= 0) {
 40             binCount = 1;
 41             for (Node<K,V> e = f, pred = null;; ++binCount) {
 42               K ek;
 43               if (e.hash == h &&
 44                   ((ek = e.key) == key ||
 45                    (ek != null && key.equals(ek)))) {
 46                 //桶内找到与key对应的node,计算value
 47                 val = remappingFunction.apply(key, e.val);
 48                 //如果value非null,则使用计算后的value替换旧的value
 49                 if (val != null)
 50                   e.val = val;
 51                 //如果value为null,则删除该node
 52                 else {
 53                   delta = -1;
 54                   Node<K,V> en = e.next;
 55                   if (pred != null)
 56                     pred.next = en;
 57                   else
 58                     setTabAt(tab, i, en);
 59                 }
 60                 break;
 61               }
 62               pred = e;
 63               //桶内没有找到key对应的node,根据key计算value,
 64               //如果value非null则将该key-value加入桶内,如果value为null则什么都不做
 65               if ((e = e.next) == null) {
 66                 val = remappingFunction.apply(key, null);
 67                 if (val != null) {
 68                   delta = 1;
 69                   pred.next =
 70                       new Node<K,V>(h, key, val, null);
 71                 }
 72                 break;
 73               }
 74             }
 75           }
 76           else if (f instanceof TreeBin) {
 77             binCount = 1;
 78             TreeBin<K,V> t = (TreeBin<K,V>)f;
 79             TreeNode<K,V> r, p;
 80             if ((r = t.root) != null)
 81               p = r.findTreeNode(h, key, null);
 82             else
 83               p = null;
 84             V pv = (p == null) ? null : p.val;
 85             val = remappingFunction.apply(key, pv);
 86             if (val != null) {
 87               if (p != null)
 88                 p.val = val;
 89               else {
 90                 delta = 1;
 91                 t.putTreeVal(h, key, val);
 92               }
 93             }
 94             else if (p != null) {
 95               delta = -1;
 96               if (t.removeTreeNode(p))
 97                 setTabAt(tab, i, untreeify(t.first));
 98             }
 99           }
100           }
101       }
102       if (binCount != 0) {
103         if (binCount >= TREEIFY_THRESHOLD)
104           treeifyBin(tab, i);
105         break;
106       }
107     }
108   }
109   if (delta != 0)
110     addCount((long)delta, binCount);
111   return val;
112 }
View Code

 

 1 protected void enforceCapacity() {
 2   StoreEventSink<K, V> eventSink = storeEventDispatcher.eventSink();
 3   try {
 4       //ATTEMPT_RATIO为4,即最多尝试evict4次,EVICTION_RATIO为2,即最多evict2个元素,capacity即我们设置的<heap unit="entries">x</heap>数,map.naturalSize()是当前已映射数
 5     for (int attempts = 0, evicted = 0; attempts < ATTEMPT_RATIO && evicted < EVICTION_RATIO
 6             && capacity < map.naturalSize(); attempts++) {
 7       //如果evict成功,evicted++
 8       if (evict(eventSink)) {
 9         evicted++;
10       }
11     }
12     storeEventDispatcher.releaseEventSink(eventSink);
13   } catch (RuntimeException re){
14     storeEventDispatcher.releaseEventSinkAfterFailure(eventSink, re);
15     throw re;
16   }
17 }

 

 1 boolean evict(final StoreEventSink<K, V> eventSink) {
 2   evictionObserver.begin();
 3   //产生的随机数用于确定首个样本的index
 4   final Random random = new Random();
 5 
 6     //第一轮采样。
 7     //SAMPLE_SIZE为8,表示最少采样8个样本(如果样本不足就8个,采完就行),
 8     //EVICTION_PRIORITIZER是一个Comparator,会比较node的lastAccessTime,
 9     //EVICTION_ADVISOR即evict建议,可以自定义,
10     //第一轮采样会接收evict建议,如果第一轮年采样没evict的建议都是不evict,
11     //则进行第二轮采样,第二轮采样会忽略evict建议。
12     //注意,evictionAdvice在value存入时就已确定,即valueHolder中持有evictionAdvice(boolean)
13   Map.Entry<K, OnHeapValueHolder<V>> candidate = map.getEvictionCandidate(random, SAMPLE_SIZE, EVICTION_PRIORITIZER, EVICTION_ADVISOR);
14 
15   if (candidate == null) {
16       //第二轮采样
17     // 2nd attempt without any advisor
18     candidate = map.getEvictionCandidate(random, SAMPLE_SIZE, EVICTION_PRIORITIZER, noAdvice());
19   }
20 
21   if (candidate == null) {
22     return false;
23   } else {
24       //根据key删除元素
25     final Map.Entry<K, OnHeapValueHolder<V>> evictionCandidate = candidate;
26     final AtomicBoolean removed = new AtomicBoolean(false);
27     map.computeIfPresent(evictionCandidate.getKey(), new BiFunction<K, OnHeapValueHolder<V>, OnHeapValueHolder<V>>() {
28       @Override
29       public OnHeapValueHolder<V> apply(K mappedKey, OnHeapValueHolder<V> mappedValue) {
30         if (mappedValue.equals(evictionCandidate.getValue())) {
31           removed.set(true);
32           if (!(evictionCandidate.getValue() instanceof Fault)) {
33             eventSink.evicted(evictionCandidate.getKey(), evictionCandidate.getValue());
34             invalidationListener.onInvalidation(mappedKey, evictionCandidate.getValue());
35           }
36           updateUsageInBytesIfRequired(-mappedValue.size());
37           return null;//return null会删除
38         }
39         return mappedValue;
40       }
41     });
42     if (removed.get()) {
43       evictionObserver.end(StoreOperationOutcomes.EvictionOutcome.SUCCESS);
44       return true;
45     } else {
46       evictionObserver.end(StoreOperationOutcomes.EvictionOutcome.FAILURE);
47       return false;
48     }
49   }
50 }

 

 1 public Entry<K, V> getEvictionCandidate(Random rndm, int size, Comparator<? super V> prioritizer, EvictionAdvisor<? super K, ? super V> evictionAdvisor) {
 2   Node<K,V>[] tab = table;
 3   if (tab == null || size == 0) {
 4     return null;
 5   }
 6 
 7   K maxKey = null;
 8   V maxValue = null;
 9 
10   int n = tab.length;
11   int start = rndm.nextInt(n);
12 
13   Traverser<K, V> t = new Traverser<K, V>(tab, n, start, n);
14   //advance()可以得到下一个node(下一个node有两种情况,1桶内,直接通过next得到,2桶内的next==null,则遍历下一个桶)
15   for (Node<K, V> p; (p = t.advance()) != null;) {
16     K key = p.key;
17     V val = p.val;
18     //adviseAgainstEviction即不建议evict
19     if (!evictionAdvisor.adviseAgainstEviction(key, val)) {
20         //通过prioritizer(Comparator)的比较,得到lastAccessTime最小的
21       if (maxKey == null || prioritizer.compare(val, maxValue) > 0) {
22         maxKey = key;
23         maxValue = val;
24       }
25       //虽然已经样本数已经达到要求,但是仍然继续遍历当前桶内节点(t.index==terminalIndex)
26       if (--size == 0) {
27         for (int terminalIndex = t.index; (p = t.advance()) != null && t.index == terminalIndex; ) {
28           key = p.key;
29           val = p.val;
30           if (!evictionAdvisor.adviseAgainstEviction(key, val) && prioritizer.compare(val, maxValue) > 0) {
31             maxKey = key;
32             maxValue = val;
33           }
34         }
35         return new MapEntry<K, V>(maxKey, maxValue, this);
36       }
37     }
38   }
39 
40   return getEvictionCandidateWrap(tab, start, size, maxKey, maxValue, prioritizer, evictionAdvisor);
41 }

 

转载于:https://www.cnblogs.com/holoyong/p/7424075.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值