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 }