Guava Cache实现原理——高效回收技巧

系列文章:

Guava Cache实现原理——开篇&基本实现

Guava Cache实现原理——LRU回收实现

Guava Cache实现原理——引用类型回收

Guava Cache实现原理——高效回收技巧

 

目录

一、前言

二、实现

1、被动回收设计

2、利用用户CPU时钟回收设计

3、类ThreadLocal回收设计

三、惯例


 

一、前言

通过前面三篇文章的介绍,想必大家对Guava Cache的实现原理已经有了更进一步的认识了。特别是Guava Cache中对缓存项的回收方案的实现原理。今天我们将做本专题的最后一篇文章,本文将为大家总结与分析Guava Cache的缓存项的回收设计技巧。

 

二、实现

总体来看,在Guava Cache的回收方案设计中,主要有如下三个方案的高效回收技巧。接下来我们一起看看其实现细节。

1、被动回收设计

在Guava Cache中被设计成了被动回收,即不主动回收(业务可以自己发起主动回收,但是会影响性能)。这个实现和Redis中的过期key的被动回收策略有点类似(Redis中主动回收策略-定时回收,但每次不会回收所有的过期项)。所以熟悉Redis被动回收的同学都应该知道了为什么Guava Cache要采用被动回收了。

其实原因也很简单,我们都知道在执行回收操作需的过程中都要持有分段锁。那么如果我们后端有线程主动去回收过期项,那么势必会多读取线程产生一定的影响,如果过期线程处理的不好甚至可能导致用户读取线程大量延迟。那么如果要确保高效的读取,又要实现主动回收功能,那么就必须要花很多的时间和尽力来实现这个主动功能。因此会增加Guava Cache的实现复杂度。

所以为了能够简单的实现高效缓存,Guava Cache最终选择了被动回收。我们都知道被动回收会产生一些内存空间浪费,因为一些过期缓存项可能不能被及时删除。所以Guava Cache的被动回收设计是在内存空间和简单高效之间做了一个选择。简单来看,还有点类型”以空间换取时间“。

2、利用用户CPU时钟回收设计

在被动回收的设计中,我们每次访问(读写)缓存项的时候,都会尝试对缓存项进行回收,这个设计就是窃取了用户时间来回收。即利用用户访问的时候的CPU时钟对缓存项进行回收。

在这个设计中,有一个比较好的设计,就是每次在get缓存项后,都会尝试一下tryLock去获取分段锁。如果获取到了分段锁,则会执行一次过期缓存项的回收。每次回收都会将该Segent下所有引用类型的key和已经过期的缓存项都回收了。因为所有应用类型被回收的缓存项都可以通过Queue获取到,已经过期的缓存项可以通过writeQueue和acessQueue链表(链表按时间有序)获取到,因此虽然回收了当前Segment下所有需要回收的缓存项,但是也不会很耗时间。

3、类ThreadLocal回收设计

想必属性ThreadLocal的同学都知道,TheadLocal中使用了一个自定义的HashMap来存储同一个线程中的多个TheadLocal。其key=ThreadLocal(通过弱引用存储),value=Value(ThreadLocal存储的值)。ThreadLocal的实现整体就只有一个数组(没有链表),在ThreadLocal中Hash冲突不是用链表解决,而通过在数组中向后位移的方式(具体这不详解解读)。

当从TheadLocal中访问某个数据时,因为存在Hash冲突,因为不断在数组中向后位移来找到自己需要的值,在这个过程中它会随便把她访问到的数组回收了(如果该项需要被回收)。

而Guava Cache也采用了类似的回收策略,在Guava Cache需要回收(删除)某个缓存项的时候,当Hash冲突之后,其需要从链表中一次往下查找该缓存项,再查找的过程中也会随便把扫描到缓存项回收(如果需要回收)了。

Guava Cache实现原理——高效回收技巧_第1张图片

Guava Cache实现原理——高效回收技巧_第2张图片

三、惯例

如果你对本文有任何疑问或者高见,欢迎添加公众号共同交流探讨(添加公众号可以获得”Java高级架构“上10G的视频和图文资料哦)。

 

 

你可能感兴趣的:(#,Guava)