• 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 }
  • 相关阅读:
    微信小程序组件loading
    微信小程序组件toast
    微信小程序组件modal
    Thread was being aborted.
    Linux(Contos7.5)环境搭建之Linux远程登录(一)
    Method 'ExecuteAsync' in type 'System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy' does not have an implementation
    Cannot find class [org.springframework.http.converter.json.MappingJacksonHttpMessageConverter]
    Visual Studio 调试时无法命中断点
    springjdbc使用c3p0连接池报错 java.lang.NoClassDefFoundError: com/mchange/v2/ser/Indirector
    JUnit initializationError错误
  • 原文地址:https://www.cnblogs.com/holoyong/p/7424075.html
Copyright © 2020-2023  润新知