GuavaCache核心原理之数据结构
Guava Cache的数据结构跟ConcurrentHashMap类似,但也不完全一样。最基本的区别是ConcurrentMap会一直保存所有添加的元素,直到显式地移除。
相对地,Guava Cache为了限制内存占用,通常都设定为自动回收元素。其数据结构图如下:
1)LocalCache为Guava Cache的核心类,包含一个Segment数组组成
2)Segement数组的长度决定了cache的并发数
3)每一个Segment使用了单独的锁,其实每个Segment继承了ReentrantLock,对Segment的写操作需要先拿到锁
4)每个Segment由一个table和5个队列组成
5)5个队列:
ReferenceQueue keyReferenceQueue : 已经被GC,需要内部清理的键引用队列
ReferenceQueue valueReferenceQueue : 已经被GC,需要内部清理的值引用队列
ConcurrentlinkedQueue<ReferenceEntry<k,v>> recencyQueue : LRU队列,当segment上达到临界值发生写操作时该队列会移除数据
Queue<ReferenceEntry<K, V>> writeQueue:写队列,按照写入时间进行排序的元素队列,写入一个元素时会把它加入到队列尾部
Queue<ReferenceEntry<K, V>> accessQueue:访问队列,按照访问时间进行排序的元素队列,访问(包括写入)一个元素时会把它加入到队列尾部
6)1个table:
AtomicReferenceArray<ReferenceEntry<K, V>> table:AtomicReferenceArray可以用原子方式更新其元素的对象引用数组
7)ReferenceEntry<k,v>
ReferenceEntry是Guava Cache中对一个键值对节点的抽象,每个ReferenceEntry数组项都是一条ReferenceEntry链。并且一个ReferenceEntry包含key、hash、valueReference、next字段(单链)
Guava Cache使用ReferenceEntry接口来封装一个键值对,而用ValueReference来封装Value值
GuavaCache核心原理之回收机制
Guava Cache提供了三种基本的缓存回收方式:
1)基于容量回收
在缓存项的数目达到限定值之前,采用LRU的回收方式
2)定时回收
expireAfterAccess:缓存项在给定时间内没有被读/写访问,则回收。回收顺序和基于大小回收一样(LRU)
expireAfterWrite:缓存项在给定时间内没有被写访问(创建或覆盖),则回收
3)基于引用回收
通过使用弱引用的键、或弱引用的值、或软引用的值,Guava Cache可以垃圾回收
除了以上三种还有主动删除,采用命令,这个前面讲了
GuavaCache构建的缓存不会"自动"执行清理和回收工作,也不会在某个缓存项过期后马上清理,也没有诸如此类的清理机制。
GuavaCache是在每次进行缓存操作的时候,惰性删除 如get()或者put()的时候,判断缓存是否过期。
GuavaCache核心原理之Segment定位
先通过key做hash定位到所在的Segment
通过位运算找首地址的偏移量 SegmentCount>=并发数且为2的n次方
V get(K key, CacheLoader<super K, V> loader) throws ExecutionException {
// 注意,key不可为空
int hash = hash(checkNotNull(key));
// 通过hash定位到segment数组的某个Segment元素,然后调用其get方法
return segmentFor(hash).get(key, hash, loader);
}
再找到segment中的Entry链数组,通过key的hash定位到某个Entry节点
V get(K key, int hash, CacheLoader<super K, V> loader) throws ExecutionException {
checkNotNull(key);
checkNotNull(loader);
try {
if (count != 0) { // read-volatile
// 内部也是通过找Entry链数组定位到某个Entry节点
ReferenceEntry<K, V> e = getEntry(key, hash);
......