当前位置: 首页 > news >正文

详细介绍:性能优化 - 案例篇:缓存_Guava#LoadingCache设计

详细介绍:性能优化 - 案例篇:缓存_Guava#LoadingCache设计

在这里插入图片描述


Pre

性能优化 - 理论篇:常见指标及切入点

性能优化 - 理论篇:性能优化的七类技术手段

性能优化 - 理论篇:CPU、内存、I/O诊断手段

性能优化 - 工具篇:常用的性能测试工具

性能优化 - 工具篇:基准测试 JMH

性能优化 - 案例篇:缓冲区


  1. 引言:解释缓存与缓冲的区别及缓存在性能优化中的重要性;
  2. 缓存基本概念:缓存的本质、应用场景、进程内 vs 进程外缓存;
  3. Guava LoadingCache(LC)示例:
    3.1 引入依赖与初始化配置;
    3.2 手动 put 与自动加载(CacheLoader)模式;
    3.3 缓存容量、初始大小与并发级别设置;
    3.4 缓存移除与监听(invalidate + removalListener);
  4. 缓存回收策略:
    4.1 基于容量的回收(LRU);
    4.2 基于时间的回收(expireAfterWrite / expireAfterAccess);
    4.3 基于 JVM GC 的回收(weakKeys / weakValues / softValues);
    4.4 GC 回收引发的缓存颠簸问题与解决思路;
  5. 缓存算法简述:FIFO、LRU、LFU 三种常见策略;
  6. 用 LinkedHashMap 实现简易 LRU:
    6.1 LinkedHashMap 构造参数与访问顺序;
    6.2 覆盖 removeEldestEntry 实现容量控制;
    6.3 线程安全与功能局限性说明;
  7. 操作系统层面的预读与文件缓存:readahead 机制、完全加载策略;
  8. 缓存优化一般思路:何时用缓存、容量与命中率考量;
  9. 缓存的一些注意事项与示例:HTTP 304、CDN;

引言

在性能优化 - 案例篇:缓冲区中,介绍了“缓冲”这一优化手段——通过将数据暂存到内存缓冲区,批量顺序读写来缓解设备间的速度差异。与缓冲相伴随的“孪生兄弟”就是缓存(Cache)。缓存将常用数据放到相对高速的存储层(如内存)中,从而在后续访问时实现瞬时读取,显著提升性能。

举例而言:

缓存几乎是软件中最常见的优化技术。从 CPU L1/L2/L3 缓存到 Redis、Memcached 这样的分布式缓存,无不围绕“速度差异协调”这一核心展开。

接下来我们主要聚焦于进程内缓存——堆内缓存,以 GuavaLoadingCache 为示例讲解堆内缓存设计思路、常见回收策略和算法实现。


1. 缓存基本概念

缓存的核心作用,是在两个速度差异巨大的组件之间增加一层高速存储:

根据缓存所在的物理位置,可将其分为:

  1. 进程内缓存(堆内缓存):直接存放在 JVM 堆里,访问速度最快,但容量受限于可用堆内存;
  2. 进程外缓存(进程间或分布式缓存):如 Redis、Memcached,通常运行在独立进程或集群里,通过网络访问,虽然速度比数据库快许多,但仍比堆内缓存慢一个数量级;

接下来重点讲解 进程内缓存,常见实现包括 Guava Cache、Caffeine、Ehcache、JCache 等。它们都提供了基于内存分片、高并发访问、灵活回收策略和统计监控的堆内缓存解决方案。

在这里插入图片描述


2. Guava 的 LoadingCache

在这里插入图片描述

Guava 提供了功能强大的 Cache 接口和 LoadingCache 实现,既支持手动存入(put()),也支持在缓存未命中时“自动加载”(CacheLoader)。下面通过示例逐步介绍其用法与内部配置要点。

2.1 引入依赖与初始化

首先,通过 Maven 将 Guava 库加入项目:

<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>29.0-jre</version>
</dependency>

然后,使用 CacheBuilder 来创建一个 LoadingCache

LoadingCache<
String
, String> lc = CacheBuilder
.newBuilder(
)
// 设置最大缓存容量:达到上限后回收其他元素
.maximumSize(1000
)
// 设置初始容量:底层 Hash 表的初始大小为 16(默认)
.initialCapacity(16
)
// 设置并发级别:将缓存分片成 4 个 segment,提升并发读写性能
.concurrencyLevel(4
)
.build(
new CacheLoader<
String
, String>(
) {
@Override
public String load(String key)
throws Exception {
// 缓存未命中时,自动调用 slowMethod 从外部数据源加载
return slowMethod(key)
;
}
}
)
;
  • maximumSize(int):指定 缓存中可保留条目的最大数量,一旦超过,将根据回收策略(默认 LRU)移除旧元素;
  • initialCapacity(int):指定底层哈希表初始大小(bucket 数量),避免在缓存初始化时反复扩容;
  • concurrencyLevel(int):指定并发写的“分段”数,Guava 会将内部数据结构拆分为 concurrencyLevel 个部分,以减少并发冲突;

2.2 手动 put 与自动加载(CacheLoader)

LoadingCache 支持两种获取方式:

  1. 手动 put()

    lc.put("key1"
    , "value1"
    )
    ;
    String v = lc.getIfPresent("key1"
    )
    ;
    // 立即返回 "value1"

    在这种模式下,开发者负责将外部数据同步写入缓存。

  2. 自动加载 get()

在这里插入图片描述

// 第一次调用:缓存中无 key "a",触发 CacheLoader.load("a")
long start = System.nanoTime(
)
;
String result1 = lc.get("a"
)
;
// slowMethod("a") 需 1s
System.out.println("第一次调用耗时: " + (System.nanoTime(
) - start)
)
;
// 第二次调用:缓存命中,迅速返回
long start2 = System.nanoTime(
)
;
String result2 = lc.get("a"
)
;
System.out.println("第二次调用耗时: " + (System.nanoTime(
) - start2)
)
;

其中 load(String key) 方法可同步加载所需数据(如从数据库或外部 API 拉取),并在返回值后自动存入缓存。

2.2.1 示例代码

public
class GuavaCacheDemo {
// 模拟一个缓慢方法:睡眠 1 秒后返回结果
static String slowMethod(String key)
throws Exception {
Thread.sleep(1000
)
;
return key + ".result"
;
}
public
static
void main(String[] args)
throws Exception {
LoadingCache<
String
, String> lc = CacheBuilder
.newBuilder(
)
.maximumSize(1000
)
.initialCapacity(16
)
.concurrencyLevel(4
)
.recordStats(
) // 开启统计信息收集
.build(
new CacheLoader<
String
, String>(
) {
@Override
public String load(String key)
throws Exception {
return slowMethod(key)
;
}
}
)
;
// 第一次 get,会调用 slowMethod
long t1 = System.nanoTime(
)
;
String v1 = lc.get("a"
)
;
long elapsed1 = System.nanoTime(
) - t1;
System.out.println("第一次 get 用时: " + elapsed1 + " ns"
)
;
// 第二次 get,立即返回
long t2 = System.nanoTime(
)
;
String v2 = lc.get("a"
)
;
long elapsed2 = System.nanoTime(
) - t2;
System.out.println("第二次 get 用时: " + elapsed2 + " ns"
)
;
// 输出命中率与加载次数等统计
System.out.println("Cache Stats: " + lc.stats(
)
)
;
}
}
  • recordStats():开启缓存统计功能,可用于后续分析 hitRate()loadSuccessCount() 等指标;
  • CacheLoader.load():当 key 未命中时,自动调用并将结果写回缓存;

2.3 缓存移除与监听(invalidate + removalListener)


3. 缓存回收策略

在缓存容量有限的前提下,需设计合适的回收策略来剔除“冷”或不再需要的数据,以保证“热点”数据得到优先保留。Guava 原生支持多种回收方式:

3.1 基于容量的回收(LRU)


3.2 基于时间的回收


3.3 基于 JVM GC 的回收

3.3.1 GC 回收引发的缓存颠簸问题

当缓存条目使用弱引用或软引用,一旦 JVM 触发 GC,就可能一次性清空大批缓存数据。若该缓存频繁被访问,缓存将被迅速重新加载,导致连续触发多次 GC 和缓存“哗啦啦回补”的现象——CPU 消耗骤增,却无法留住数据。

解决思路


4. 常见缓存算法简介

在这里插入图片描述

除了 Guava 提供的默认 LRU,缓存领域常见还有两种算法:

4.1 FIFO(先进先出)

4.2 LRU(最近最少使用)

4.3 LFU(最近最不常用)


5. 简易 LRU 实现——LinkedHashMap

在 Java 中,要实现一个轻量级的 LRU 缓存,最便捷的方式是利用 LinkedHashMap 提供的“访问顺序”功能:

public
class LRUCache<
K
, V>
extends LinkedHashMap<
K
, V> {
private
final
int capacity;
public LRUCache(
int capacity) {
// 初始容量 16,负载因子 0.75,accessOrder=true 表示按访问顺序排列
super(16
, 0.75f
, true
)
;
this.capacity = capacity;
}
// 当 put 后,自动调用此方法判断是否需要移除最老条目
@Override
protected
boolean removeEldestEntry(Map.Entry<
K
, V> eldest) {
return size(
) > capacity;
}
}
  • 构造参数说明

    • initialCapacity:初始哈希表桶数,默认 16;
    • loadFactor:负载因子(0.75f);
    • accessOrder=true:按照“访问顺序”保持双向链表;
  • removeEldestEntry:在每次 put() 后自动调用,若返回 true,则移除“最久未访问”者,即 LRU 算法的核心。

在这里插入图片描述

5.1 功能局限与线程安全


6. 操作系统层面的预读与文件缓存

在这里插入图片描述

在操作系统层面,对文件 I/O 缓存设计也非常智能,进一步支撑了高性能缓存架构。Linux 下可以通过 free 命令查看内存状态,其中 cached 区域往往十分庞大:

$ free -h
total        used        free      shared  buff/cache   available
Mem:           16Gi       4.2Gi       2.5Gi       128Mi       9.4Gi       11Gi
Swap:         2.0Gi       512Mi       1.5Gi
  • buff/cache:表示操作系统将磁盘块缓存在内存中的容量,包括文件系统页缓存、目录缓存等;
  • Readahead(预读):当进程顺序读取文件时,内核会自动尝试以“页面”为单位预读后续若干页面,以减少随机读时的磁盘寻道开销。
    在这里插入图片描述

例如,若应用以 4KB 为单位读取大型文件,内核可能在后台提前载入 128KB 或更多连续页面,由此实现“顺序读取性能≈内存读取性能”的效果。

  • 完全加载策略:若某一小文件(如几 MB)访问频率极高,可在应用启动时一次将整个文件 mmap()read() 到内存(posix_fadvise(..., POSIX_FADV_WILLNEED)),确保后续访问都命中内核缓存;

7. 缓存优化的一般思路

在实际项目中,引入进程内缓存时,可根据以下几个准则:

  1. 缓存目标场景

  2. 缓存大小与容量

在这里插入图片描述

  1. 回收策略选择

  2. 监控与统计

  3. 缓存失效与一致性

  4. 预加载 vs 惰性加载


8. 缓存应用案例

  1. HTTP 304(Not Modified)缓存

  2. CDN(内容分发网络)缓存

  3. 数据库二级缓存


9. 小结

进程内缓存(堆内缓存) 的设计思路与常见实现:

  1. 缓存本质与应用场景

  2. Guava LoadingCache

  3. 常见缓存算法

  4. 简易 LRU 实现

  5. 操作系统缓存与预读

  6. 缓存优化一般思路

  7. 示例应用

在这里插入图片描述

http://www.hskmm.com/?act=detail&tid=24745

相关文章:

  • 2025年X射线管厂家最新企业品牌推荐排行榜,工业用金属陶瓷,波长色散荧光分析,应力衍射分析,管板角焊缝,轮胎检测,辐照,固定阳极波纹陶瓷,测厚,食品检测 X 射线管公司推荐
  • AtCoder Beginner Contest 400
  • P14134 题解——随机化太帅了
  • 2025 年北京档案存放公司 升职猫档案服务平台:16 年老牌机构的合规服务与高效解决方案解析
  • 2025电容厂家最新品牌推荐排行榜白皮书,固态,高压,牛角,安规,CBB,超级,红宝石电解,螺栓,超级电容推荐这十家公司!
  • bug汇总
  • 2025石材加工厂家最新品牌推荐排行榜:大祥工艺,业务覆盖东北,辽宁盖州,专业浮雕雕刻高级技师
  • centos7升级降级内核 centos升级降级内核 centos升级内核 centos降级内核
  • Photoshop 在线网页版?是的,它来了!免费使用指南
  • 基于Python+Vue开发的母婴商城管理系统源码+运行步骤
  • 2025防火隔断品牌最新推荐排行榜:甲级防火玻璃隔断厂家深度解析,精选优质品牌助力采购决策!
  • 机器学习Day5-模型诊断 - 详解
  • 这些行业软件你用过哪个
  • 提供远程服务
  • 分享一些软件资讯
  • 2025美缝剂源头厂家最新推荐权威排行榜:深度解析五大品牌实力与选购指南
  • 2025数控铣床厂家最新企业品牌推荐排行榜, 双头数控铣床,双面数控铣床,龙门数控铣床,双侧数控铣床推荐这十家公司!
  • 2025最新推荐点胶机源头厂家权威排行榜:涵盖自动点胶机,果冻胶,无痕内衣,热熔胶类设备,助力企业精准挑选优质厂家
  • 前端开源JavaScrip库 - 详解
  • Probabilistic method小记
  • 数据生成方法初步调研
  • Elastic Stack 9.1.4 发布:重要安全更新与功能优化
  • 2025钛白粉源头厂家最新推荐排行榜:覆盖广东珠三角东莞华南深圳长三角地区的优质供应商解析
  • 完整教程:图论回溯
  • 用 Whisper 打破沉默:AI 语音技术如何重塑无障碍沟通方式? - 指南
  • 做题记录(Oct.)
  • 生成式AI改进极端多标签分类技术
  • 2025.10.5——1绿
  • 【清晰教程】利用Git工具将本地项目push上传至GitHub仓库中 - 指南
  • 题解:2025.10.信友队.智灵班选拔面试题目