guava cache 缓存回收及原理实现

本文详细介绍了Guava Cache的功能特点,包括并发策略、缓存过期机制、缓存移除机制及缓存统计等。通过具体用例展示了如何利用CacheLoader和Callable加载缓存,并深入分析了基于容量、时间和引用的缓存回收策略。最后探讨了Guava Cache的回收原理与实现。

 

一、简介

Guava cache 是一个全内存本地缓存。它拥有并发策略、缓存过期机制、缓存移除机制、缓存统计等功能.

缓存的优势:

1、减少网络传输的开销

2、减少数据序列化和反序列化

3、加快了访问速度(与数据库、文件系统相比)。

缓存的使用场景:

1、缓存全量数据

2、缓存热点数据

二、基本用法

1、缓存加载

这两种方法都实现一种逻辑:从缓存中取key的值,如果该key已经缓存过,直接返回缓存中的值,

如果没有缓存该key,可以通过某个方法来获取这个值。

  • CacheLoader

  • Callable

2、策略分析

  • expireAfterAccess 读写缓存后多久过期

  • expireAfterWrite 写缓存后多久过期

    并发时的特性:(无论缓存中是否存在数据)如果当其他线程在加载数据,当前线程会一直阻塞等待其它线程加载完成数据

     

缺点:导致缓存穿透

  • refreshAfterWrite 写缓存后多久刷新数据,只阻塞加载数据线程,其他线程返回旧值

    并发时的特性:

    a、缓存中没有数据时(初始化缓存数据时),如果其它线程在加载数据的时候,当前线程会一直阻塞等待其它线程加载数据完成。

    b、缓存中有数据(已经初始化缓存中的数据时),如果当前线程正在加载数据,其它线程返回旧数据。

     

缺点:导致数据过旧的问题

3、缓存回收

  • 基于容量回收

    CacheBuilder.maxmumSize(long)

  • 基于时间回收

    CacheBuilder.expireAfterAccess()

    CacheBuilder.expireAfterWrite()

  • 基于引用回收

    软引用和弱引用

    CacheBuilder.weakKeys()

    当键没有其它(强或软)引用时,缓存项可以被垃圾回收。因为垃圾回收仅依赖恒等式(==),使用弱引用键的缓存用==而不是equals比较键。

    CacheBuilder.weakValues()

    CacheBuilder.softValues()

    软引用只有在响应内存需要时,才按照全局最近最少使用的顺序回收。使用软引用时需要考虑性能影响。使用软引用值的缓存同样用==而不是equals比较值。

引用类型

描述

回收时间

用途

备注

强引用

程序中随处可见

永不回收

普通对象引用

 

软引用

描述对象有用但不是必须的

内存不足回收

缓存对象

软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,java虚拟机就会把这个软引用加入到与之关联的引用队列中

弱引用

描述对象不是非必须的

jvm垃圾回收

缓存对象

 

 

4、其它

三、缓存回收原理及实现

1、什么是LRU算法

LRU(Least recently used,最近最少使用)是核心思想是基于“如果数据最近被访问过,那将来被访问的几率也更高”。

1.1:新数据插入到链表的头部。

1.2:每当缓存命中(即缓存数据被访问),则将数据移到链表头部。

1.3: 当链表满的时候,将链表尾部的数据丢弃。

 

2、LocalCache数据结构

 

名称

类型

作用

segments

Segment<k,V>[]

实现ReentrantLock锁,减少锁的粒度,提高并发度。好处:分段锁很好保证并发读写的效率,因此支持非阻塞的读和不同段之间的并发写

table

AtomicReferenceArray<ReferenceEntry<K,V>

存放键值对的地方

referenceEntry

ReferenceEntry<K,V>

封装键值对

keyReferenceQueue

ReferenceQueue<K>

已经被GC,需要内部清理key引用队列

valueReferenceQueue

ReferenceQueue<V>

已经被GC,需要内部清理value引用队列

recencyQueue

Queue<ReferenceEntry<K,V>>

记录当entries被访问时,去更新accessQueue中顺序。在segment中当segment上限值或是写操作发生会去更新accessQueue顺序,同时清空recencyQueue。

writeQueue

Queue<ReferenceEntry<K,V>>

按照写入时间进行排序的元素队列,写入元素时会把它加入队列的队尾

accessQueue

Queue<ReferenceEntry<K,V>>

按照访问时间进行排序的元素队列,访问或是写入元素时会把它加入到队列的队尾。

3、segment如何清理(evict) entry

evict方式

使用cacheBuilder构建的缓存不会“自动”执行清理和回收工作,也不会在某个缓存项过期后马上清理。

相反,它会在写操作时顺带做少量的维护工作,或者偶尔在读操作时做——如果写操作实在太少的话。

理由:如果guava使用开启一个后台线程每隔一段时间来扫描一次table以evict哪些已经expire的entry,

这样的增加资源的消耗。

evict对象

基于引用回收

keyReferenceQueue

valueReferenceQueue

基于时间回收

writeQueue

accessQueue

 

evict流程

基于引用和时间回收策略

put开始时

 

get结束之前

 

基于容量回收策略(LRU)

put结束之前

前提条件:在设置maxmumSize或maximumWeight时,才会进行该操作。

weight的作用

1. 对weight值为0时,在计算因为size limit而evict是忽略该Entry(它可以通过其他机制evict)。

2. 如果设置了maximumWeight值,则当Cache中weight和超过了该值时,就会引起evict操作。

 

四、总结

设计缓存系统中过期机制时,可以考虑在读和写操作进行清理操作,从而提供效率。

 

参考文档:

【1】、google guava cache缓存基本使用讲解

【2】、[Google Guava] 3-缓存

【3】、CachesExplained

【4】、自定义LRU算法的缓存实现

【5】、Google Guava Cache 全解析

【6】、Java Cache系列之Guava Cache实现详解

【7】、GuavaCache

【8】、Guava cache 机制及源码分析

【9】、Java的引用类型

【10】、Guava Cache 的缓存管理与使用

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值