reflect包-WeakCache类

it2022-05-05  206

前驱知识点

java 引用的四种基本类型

java 中包括四种基本类型,分别是FinalReference(强),SoftReference(软),WeakReference(弱)、PhantomReference(虚),按照顺序引用能力依次递减。

FinalReference

产生:使用new 关键字或者显示使用FinalReference 构建的对象应用特点:强引用可以直接访问目标对象;强引用所指向的对象在任何时候都不会被系统回收。JVM宁愿抛出OOM异常,也不会回收强引用所指向的对象;强引用可能导致内存泄漏;使用

SoftReference

产生: Person p = new Person(); SoftReference sf = new SoftReference(p) p = null; System.gc(); System.out.println("值=" + sf.get()) 特点:软引用是除了强引用外,最强的引用类型。可以通过java.lang.ref.SoftReference使用软引用。一个持有软引用的对象,不会被JVM很快回收,JVM会根据当前堆的使用情况来判断何时回收。当堆使用率临近阈值时,才会去回收软引用的对象。因此,软引用可以用于实现对内存敏感的高速缓存。 SoftReference的特点是它的一个实例保存对一个Java对象的软引用, 该软引用的存在不妨碍垃圾收集线程对该Java对象的回收。也就是说,一旦SoftReference保存了对一个Java对象的软引用后,在垃圾线程对 这个Java对象回收前,SoftReference类所提供的get()方法返回Java对象的强引用。一旦垃圾线程回收该Java对象之后,get()方法将返回null 知识点: System.gc() 会通知虚拟机进行full gc,full gc 会损耗较大的性能。

WeakReference

产生 Person p = new Person(); WeakReference wf = new WeakReference(p); p = null; System.out.println("full gc前="+wf.get()) System.gc(); Thread.sleep(1000); System.out.println("full gc后=" + wf.get()) 特点: 当发生full gc 时会回收弱引用,在这之前将会可访问。

PhantomReference(phantom: 幻影/幻象)

使用: public class TestReference { public final static void main(String[] args) throws InterruptedException { Object o = new Object(); ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>(); PhantomReference sf = new PhantomReference(o, referenceQueue); o = null; System.out.println("before full gc=" + sf.get()); System.out.println("has put to queue=" + referenceQueue.poll()); System.gc(); // 等待虚拟机调度gc完成 Thread.sleep(1000); System.out.println("has put to queue="+referenceQueue.poll()); System.out.println("after full gc=" + sf.get()); } }

输出如下:

before full gc=null has put to queue=null has put to queue=java.lang.ref.PhantomReference@610455d6 after full gc=null 特点:一个持有虚引用的对象,和没有引用几乎是一样的,随时可能被垃圾回收器回收。当试图通过虚引用的get()方法取得强引用时,总是会失败。并且,虚引用必须和引用队列一起使用,它的作用在于跟踪垃圾回收过程。 当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在垃圾回收后,销毁这个对象,将这个虚引用加入引用队列。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动

WeakCache 类基本介绍

初次接触WeakCache类是在理解Java proxy的源码时看到的,要想理解Proxy的原理,WeakCache是过不去的坎,因为Proxy获取代理类的Class 实例通过代码:

// 如果代理类在给定的Loader和接口下已经存在,那么直接放回它的副本, // 否则它将使用ProxyClassFactory 创建 return proxyClassCache.get(loader, interfaces);

而 proxyClassCache 正是WeakCache的实例

/** * a cache of proxy classes */ private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

接下来看看WeakChache是什么

WeakCache顾名思义,就是一个保存WeakReference值的缓存类,它接收三个泛型值:key, p代表参数类型,V 代表缓存值类型,它保存值的形式有点类似Map 的key,value,只不过它是一个变体:(key,(subKey->value)) 这样的形式,key,value都是weakReference,sub-key强引用(发现在Proxy类中sub-key也是weak),但实际情况subKey的类型是否subKeyFactory决定的(subkey由subKeyFactory),value 是由valueFactory决定的,而key则一定weak类型.

它只有一个构造函数用来接收subKeyFactory函数以及valueFactory函数.

final class WeakCache<K, P, V> { // 队列,用来记录gc回收后的对象key private final ReferenceQueue<K> refQueue = new ReferenceQueue<>(); // key 是Ojbect类型,支持null,次结构代表(key,(subkey,value) private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map = new ConcurrentHashMap<>(); private final ConcurrentMap<Supplier<V>, Boolean> reverseMap = new ConcurrentHashMap<>(); private final BiFunction<K, P, ?> subKeyFactory; 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 valueFactory a 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); } }

再来看看主要的 get 方法

public V get(K key, P parameter) { Objects.requireNonNull(parameter); 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) { ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey, valuesMap = new ConcurrentHashMap<>()); if (oldValuesMap != null) { valuesMap = oldValuesMap; } } // create subKey and retrieve the possible Supplier<V> stored by that // subKey from valuesMap Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); Supplier<V> supplier = valuesMap.get(subKey); Factory factory = null; while (true) { if (supplier != null) { // supplier might be a Factory or a CacheValue<V> instance V value = supplier.get(); if (value != null) { return value; } } // else no supplier in cache // or a supplier that returned null (could be a cleared CacheValue // or a Factory that wasn't successful in installing the CacheValue) // lazily construct a Factory if (factory == null) { factory = new Factory(key, parameter, subKey, valuesMap); } if (supplier == null) { supplier = valuesMap.putIfAbsent(subKey, factory); if (supplier == null) { // successfully installed Factory supplier = factory; } // else retry with winning supplier } else { if (valuesMap.replace(subKey, supplier, factory)) { // successfully replaced // cleared CacheEntry / unsuccessful Factory // with our Factory supplier = factory; } else { // retry with current supplier supplier = valuesMap.get(subKey); } } } }

这里主要步骤如下:

验证P 类型的值不能为null擦除无效的缓存值(weakReference发生fullgc会回收掉)根据key生成一个WeakReference类型的key,有key的hash值决定根据生成的key 获取 map 中的值,它的类型是ConcurrentMap<Object, Supplier>,它的形式是(subkey, value)进行非空判断,空的话初始化key对应的值根据key,parameter两个参数调用subKeyFactory函数来生成subkey根据subkey来获取对应的value值,while(true) 死循环直到能获取值方法return 返回,这个过程会判断 supplier 是否为空,不为空则调用get()方法返回值,其实V 是Factory类型,有这么一行代码: // lazily construct a Factory if (factory == null) { factory = new Factory(key, parameter, subKey, valuesMap); } if (supplier == null) { supplier = valuesMap.putIfAbsent(subKey, factory); if (supplier == null) { // successfully installed Factory supplier = factory; } // else retry with winning supplier }

Factory 实现了Supplier接口,并且重写了get方法,在get方法中它会调用valueFactory.apply 方法返回真正的value值:

value = Objects.requireNonNull(valueFactory.apply(key, parameter));

Supplier

Java8 提供一个函数式接口,意思是作为一个提供者,它包装某个对象值,在调用Supplier的get方法时,才会真正的返回对象,一般用来实现懒加载。


最新回复(0)