Caffeine缓存剔除策略有三个:
- 基于容量剔除
- 基于时间剔除
- 基于引用剔除
基于容量剔除
// 如果缓存的条目大于maximumSize值时,caffeine将尝试剔除最近或很少使用的缓存条目
LoadingCache<Integer, String> caches = Caffeine.newBuilder()
.maximumSize(2)
// 添加剔除缓存事件监听,cause就是剔除缓存的原因(参见RemovalCause枚举类)
.removalListener(((key, value, cause) -> {
System.out.println(cause);
}))
.build(key -> key);
设置maximumSize值,如果缓存的条目大于maximumSize值时,caffeine将尝试剔除最近或很少使用的缓存条目。
LoadingCache<Integer, String> weightCaches = Caffeine.newBuilder()
.maximumWeight(10)
.weigher(((key, value) -> (Integer)key))
.removalListener(((key, value, cause) -> {
System.out.println(cause);
}))
.build(key -> key + "val");
设置maximumWeight值,如果缓存条目的权重大于maximumWeight值时,caffeine将尝试剔除它。
PS: maximumSize和maximumWeight不能同时使用。
基于时间剔除
LoadingCache<Integer, String> accessCaches = Caffeine.newBuilder()
.expireAfterAccess(10, TimeUnit.SECONDS)
.removalListener(((key, value, cause) -> {
System.out.println(cause);
}))
.build(key -> key + "val");
expireAfterAccess:自缓存条目最后一次读取或写入时开始,如果超过了该方法设定的时长,标记条目过期。
LoadingCache<Integer, String> writeCaches = Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.SECONDS)
.removalListener(((key, value, cause) -> {
System.out.println(cause);
}))
.build(key -> key + "val");
expireAfterWrite:自缓存条目创建或最后一次写入的时间点开始,如果超过了该方法设定的时长,标记条目过期。
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.expireAfter(new Expiry<Key, Graph>() {
public long expireAfterCreate(Key key, Graph graph, long currentTime) {
// Use wall clock time, rather than nanotime, if from an external resource
long seconds = graph.creationDate().plusHours(5)
.minus(System.currentTimeMillis(), MILLIS)
.toEpochSecond();
return TimeUnit.SECONDS.toNanos(seconds);
}
public long expireAfterUpdate(Key key, Graph graph,
long currentTime, long currentDuration) {
return currentDuration;
}
public long expireAfterRead(Key key, Graph graph,
long currentTime, long currentDuration) {
return currentDuration;
}
})
.build(key -> createExpensiveGraph(key));
expireAfter(Expiry): 在可变持续时间过去之后标记条目过期。在条目的过期时间由外部资源决定的场景下使用比较理想。
基于引用的剔除
// 弱引用key和value
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.weakKeys()
.weakValues()
.build(key -> createExpensiveGraph(key));
//软引用缓存值
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.softValues()
.build(key -> createExpensiveGraph(key));
Caffeine可以对key和value使用弱引用和软引用,这样缓存可以通过垃圾回收机制实现缓存条目的回收。
caffeine缓存条目剔除的入口方法BoundedLocalCache#maintenance()
@GuardedBy("evictionLock")
void maintenance(@Nullable Runnable task) {
lazySetDrainStatus(PROCESSING_TO_IDLE);
try {
drainReadBuffer();
drainWriteBuffer();
if (task != null) {
task.run();
}
drainKeyReferences();// @1
drainValueReferences();// @2
expireEntries();// @3
evictEntries(); // @4
} finally {
if ((drainStatus() != PROCESSING_TO_IDLE) || !casDrainStatus(PROCESSING_TO_IDLE, IDLE)) {
lazySetDrainStatus(REQUIRED);
}
}
}
代码@1:释放key弱引用队列。
代码@2:释放value弱引用/软引用队列。
代码@3:根据设置的超时时间,让缓存条目超时。
代码@4:删除标记了删除标记的条目。