目录
代码环境
jdk1.8
代码阅读
1.WeakCache 是两级缓存key->(subKey->value)
2.key,value 是weakly的,subkey是strongly referenced
3.key允许null,subKey,value不允许null
package java.lang.reflect; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.BiFunction; import java.util.function.Supplier; /** * Cache mapping pairs of {@code (key, sub-key) -> value}. Keys and values are * weakly but sub-keys are strongly referenced.Keys are passed directly to * {@link #get} method which also takes a {@code parameter}. Sub-keys are * calculated from keys and parameters using the {@code subKeyFactory} function * passed to the constructor. Values are calculated from keys and parameters * using the {@code valueFactory} function passed to the constructor. * Keys can be {@code null} and are compared by identity while sub-keys returned by * {@code subKeyFactory} or values returned by {@code valueFactory} * can not be null. Sub-keys are compared using their {@link #equals} method. * Entries are expunged from cache lazily on each invocation to {@link #get}, * {@link #containsValue} or {@link #size} methods when the WeakReferences to * keys are cleared. Cleared WeakReferences to individual values dont cause * expunging, but such entries are logically treated as non-existent and * trigger re-evaluation of {@code valueFactory} on request for their * key/subKey. * * @author Peter Levart * @param <K> type of keys * @param <P> type of parameters * @param <V> type of values */ final class WeakCache<K, P, V> { //Reference引用队列,记录失效的key private final ReferenceQueue<K> refQueue = new ReferenceQueue<>(); // the key type is Object for supporting null key //缓存的底层记录数据的结构 private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map = new ConcurrentHashMap<>(); //记录了所有可用的代理类 key为代理类的CacheValue private final ConcurrentMap<Supplier<V>, Boolean> reverseMap = new ConcurrentHashMap<>(); //生成二级缓存key的工厂,这里传入的是KeyFactory private final BiFunction<K, P, ?> subKeyFactory; //生成二级缓存value的工厂, 这里传入的是ProxyClassFactory private final BiFunction<K, P, V> valueFactory; /** * Construct an instance of {@code WeakCache} * * @param subKeyFactory a function mapping a pair of *{@code (key, parameter) -> sub-key} * @param valueFactorya function mapping a pair of *{@code (key, parameter) -> value} * @throws NullPointerException if {@code subKeyFactory} or *{@code valueFactory} is null. */ public WeakCache(BiFunction<K, P, ?> subKeyFactory, BiFunction<K, P, V> valueFactory) { this.subKeyFactory = Objects.requireNonNull(subKeyFactory); this.valueFactory = Objects.requireNonNull(valueFactory); } /** * Look-up the value through the cache. This always evaluates the * {@code subKeyFactory} function and optionally evaluates * {@code valueFactory} function if there is no entry in the cache for given * pair of (key, subKey) or the entry has already been cleared. * * @param key possibly null key * @param parameter parameter used together with key to create sub-key and *value (should not be null) * @return the cached value (never null) * @throws NullPointerException if {@code parameter} passed in or *{@code sub-key} calculated by *{@code subKeyFactory} or {@code value} *calculated by {@code valueFactory} is null. */ public V get(K key, P parameter) { Objects.requireNonNull(parameter); //清除过期的缓存类加载器被回收,说明通过该类加载的类对象都已经被垃圾回收,可以全部从缓存中清除相关信息,释放不必要的对象(map中的失效key,reverseMap中持有的CacheValue) expungeStaleEntries(); Object cacheKey = CacheKey.valueOf(key, refQueue); // lazily install the 2nd level valuesMap for the particular cacheKey ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey); if (valuesMap == null) { //以CAS方式放入, 如果不存在则放入,否则返回原先的值 ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey, valuesMap = new ConcurrentHashMap<>()); //如果oldValuesMap有值, 说明其它并发线程已经提前放入了对应的值,使用成功放入的值 if (oldValuesMap != null) { valuesMap = oldValuesMap; } } // create subKey and retrieve the possible Supplier<V> stored by that // subKey from valuesMap ////根据代理类实现的接口数组来生成二级缓存key Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); Supplier<V> supplier = valuesMap.get(subKey); Factory factory = null; while (true) { if (supplier != null) { //在这里supplier可能是一个Factory也可能会是一个CacheValueFactory和CacheValue都实现了Supplier接口 V value = supplier.get(); if (value != null) { return value; } //value==null的原因 //1.当supplier是CacheValue时该类已经被卸载回收 //2.当supplier是Factory时 get()有可能返回null1)多线程下 后进入get方法的线程将返回null2)在断言关闭情况下类字节码生成失败,对应的值从valueMap中移除 } //新建一个Factory实例作为subKey对应的值 if (factory == null) { factory = new Factory(key, parameter, subKey, valuesMap); } if (supplier == null) { //subKey没有对应的值, 就将factory作为subKey的值放入 supplier = valuesMap.putIfAbsent(subKey, factory); if (supplier == null) { // 放入成功 supplier = factory; } //放入失败 supplier为valuesMap已经有的值supplier 可能是一个Factory也可能会是一个CacheValue } else { //supplier if (valuesMap.replace(subKey, supplier, factory)) { // 替换成功将factory赋值给supplier supplier = factory; } else { // 替换失败 说明其它线程已经替换将valuesMap里面的值与supplier保持一致 supplier = valuesMap.get(subKey);//可能是一个Factory(抢先放入supplier的线程,对应的get还没有执行完成),也可能会是一个CacheValue } } } } /** * Checks whether the specified non-null value is already present in this * {@code WeakCache}. The check is made using identity comparison regardless * of whether values class overrides {@link Object#equals} or not. * * @param value the non-null value to check * @return true if given {@code value} is already cached * @throws NullPointerException if value is null */ public boolean containsValue(V value) { Objects.requireNonNull(value); expungeStaleEntries(); return reverseMap.containsKey(new LookupValue<>(value)); } /** * Returns the current number of cached entries that * can decrease over time when keys/values are GC-ed. */ public int size() { expungeStaleEntries(); return reverseMap.size(); } private void expungeStaleEntries() { CacheKey<K> cacheKey; while ((cacheKey = (CacheKey<K>)refQueue.poll()) != null) { cacheKey.expungeFrom(map, reverseMap); } } /** * A factory {@link Supplier} that implements the lazy synchronized * construction of the value and installment of it into the cache. */ private final class Factory implements Supplier<V> { private final K key; private final P parameter; private final Object subKey; private final ConcurrentMap<Object, Supplier<V>> valuesMap; Factory(K key, P parameter, Object subKey, ConcurrentMap<Object, Supplier<V>> valuesMap) { this.key = key; this.parameter = parameter; this.subKey = subKey; this.valuesMap = valuesMap; } @Override public synchronized V get() { // serialize access // re-check Supplier<V> supplier = valuesMap.get(subKey); if (supplier != this) { //在这里验证supplier是否是Factory实例本身, 如果不则返回null让调用者继续轮询重试 //期间supplier可能替换成了CacheValue(并发情况下最新执行的线程会将supplier可能替换成了CacheValue,后执行get的线程不应该重复走下面的类生成逻辑), 或者由于生成代理类失败被从二级缓存中移除了 // something changed while we were waiting: // might be that we were replaced by a CacheValue // or were removed because of failure -> // return null to signal WeakCache.get() to retry // the loop return null; } // else still us (supplier == this) // create new value V value = null; try { //调用valueFactory去生成代理类, 这里会通过传入的ProxyClassFactory去生成代理类 value = Objects.requireNonNull(valueFactory.apply(key, parameter)); } finally { if (value == null) { // remove us on failure valuesMap.remove(subKey, this); } } // the only path to reach here is with non-null value assert value != null; // wrap value with CacheValue (WeakReference) CacheValue<V> cacheValue = new CacheValue<>(value); // try replacing us with CacheValue (this should always succeed) if (valuesMap.replace(subKey, this, cacheValue)) { // put also in reverseMap reverseMap.put(cacheValue, Boolean.TRUE); } else { throw new AssertionError("Should not reach here"); } // successfully replaced us with new CacheValue -> return the value // wrapped by it return value; } } /** * Common type of value suppliers that are holding a referent. * The {@link #equals} and {@link #hashCode} of implementations is defined * to compare the referent by identity. */ private interface Value<V> extends Supplier<V> {} /** * An optimized {@link Value} used to look-up the value in * {@link WeakCache#containsValue} method so that we are not * constructing the whole {@link CacheValue} just to look-up the referent. */ private static final class LookupValue<V> implements Value<V> { private final V value; LookupValue(V value) { this.value = value; } @Override public V get() { return value; } @Override public int hashCode() { return System.identityHashCode(value); // compare by identity } @Override public boolean equals(Object obj) { return obj == this || obj instanceof Value && this.value == ((Value<?>) obj).get();// compare by identity } } /** * A {@link Value} that weakly references the referent. */ private static final class CacheValue<V> extends WeakReference<V> implements Value<V> { private final int hash; CacheValue(V value) { super(value); this.hash = System.identityHashCode(value); // compare by identity } @Override public int hashCode() { return hash; } @Override public boolean equals(Object obj) { V value; return obj == this || obj instanceof Value && // cleared CacheValue is only equal to itself (value = get()) != null && value == ((Value<?>) obj).get(); // compare by identity } } /** * CacheKey containing a weakly referenced {@code key}. It registers * itself with the {@code refQueue} so that it can be used to expunge * the entry when the {@link WeakReference} is cleared. */ private static final class CacheKey<K> extends WeakReference<K> { // a replacement for null keys private static final Object NULL_KEY = new Object(); static <K> Object valueOf(K key, ReferenceQueue<K> refQueue) { return key == null // null key means we cant weakly reference it, // so we use a NULL_KEY singleton as cache key ? NULL_KEY // non-null key requires wrapping with a WeakReference : new CacheKey<>(key, refQueue); } private final int hash; private CacheKey(K key, ReferenceQueue<K> refQueue) { super(key, refQueue); this.hash = System.identityHashCode(key);// compare by identity } @Override public int hashCode() { return hash; } @Override public boolean equals(Object obj) { K key; return obj == this || obj != null && obj.getClass() == this.getClass() && // cleared CacheKey is only equal to itself (key = this.get()) != null && // compare key by identity key == ((CacheKey<K>) obj).get(); } void expungeFrom(ConcurrentMap<?, ? extends ConcurrentMap<?, ?>> map, ConcurrentMap<?, Boolean> reverseMap) { // removing just by key is always safe here because after a CacheKey // is cleared and enqueue-ed it is only equal to itself // (see equals method)... ConcurrentMap<?, ?> valuesMap = map.remove(this); // remove also from reverseMap if needed if (valuesMap != null) { for (Object cacheValue : valuesMap.values()) { reverseMap.remove(cacheValue); } } } } }1.为什么使用WeakReference 记录类加载器
我理解的,proxyClassCache是给类Proxy静态变量,而Proxy不在类加载体系里面,Proxy不能有类级别的变量强引用的任何类加载器(如果Proxy强引用了类加载器将导致该类加载器不能被垃圾回收器回收,从而导致该类加载器加载过的类都不能被回收)
2.使用WeakReference记录产生的代理类也是为了Proxy这个非类加载体现中的类干扰类的卸载
3.WeakCache的get方法的并发控制
因为它的所有会进行修改的成员变量都使用了ConcurrentMap,这个类是线程安全的。因此它将自身的线程安全委托给了ConcurrentMap, get方法尽可能的将同步代码块缩小,这样可以有效提高WeakCache的性能
如果发现错误,请联系作者