TL;DR
- 场景: 在 Java 项目中使用 Guava Cache 做本地缓存,需要理清删除策略、过期机制与常见坑
- 结论: Guava Cache 采用”懒清理 + LRU+FIFO”策略,被动删除和主动删除需要配合使用
- 产出: 删除策略全解 + 过期机制详解 + 错误速查卡
Guava Cache 数据删除机制
1. 被动删除
基于大小的删除: 当缓存元素数量达到预设最大容量时,按LRU(最近最少使用)算法自动移除最久未使用的条目。
基于时间的删除:
expireAfterAccess: 条目在指定时间内未被访问自动移除expireAfterWrite: 条目在创建或更新后超过指定时间自动移除
基于引用的删除: 使用弱引用(WeakReference)或软引用(SoftReference)包装值时,内存不足会被垃圾回收器自动回收。
2. 主动删除
- 显式调用
invalidate方法删除单个键 - 调用
invalidateAll方法批量删除 - 通过
RemovalListener监听删除事件 - 调用
cleanUp方法主动清理过期条目
重要说明:懒删除机制
Guava Cache 采用惰性删除机制。“超过过期时间后,cache.size() 仍未下降”是因为未触发访问/维护逻辑,或未调用 cleanUp。
解决方案:
- 在定时任务或关键路径补充
cache.cleanUp() - 或通过访问触发回收
错误速查表
| 症状 | 根因定位 | 修复 |
|---|---|---|
| 过期时间后 size 未下降 | 惰性删除未触发 | 调用 cleanUp() 或触发访问 |
| maximumSize 达上限但未淘汰 | LRU+FIFO 策略与预期不一致 | 打印 cache.asMap() 调试 |
| 内存占用持续升高 | 未限制 maximumSize 或过期策略 | 设置合理参数 |
应用场景示例
- 电商平台商品缓存: 使用
expireAfterWrite确保价格信息定期更新 - 用户会话管理: 使用
expireAfterAccess自动清理长时间不活跃的用户会话 - 热点数据缓存: 使用基于大小的删除防止缓存占用过多内存