1. 程式人生 > >ehcache3-源碼簡析三

ehcache3-源碼簡析三

func lap timeunit aps min ifreq ron 隨機數 unit

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 }

ehcache3-源碼簡析三