Guava LocalCache源码分析:LocalCache生成

Guava LocalCache源码分析:Cache生成

  • 版本
  • LocalCache参数说明
  • Cache构建过程
  • LocalCache介绍
    • LocalCache实例化
      • 将builder中的属性赋值到LocalCache中
      • 分段

LocalCache为guava本地缓存的解决方案,提供了基于容量,时间和引用的缓存回收方式,其数据读写都在一个进程内,相对与 redis 等分布式缓存,不需要网络传输的过程,访问速度很快,同时也受到 JVM 内存的制约,无法在数据量较多的场景下使用。

基于以上特点,guava cache 的主要应用场景为以下几种:

  • 对于访问速度有较大要求
  • 存储的数据不经常变化
  • 数据量不大,占用内存较小
  • 需要访问整个集合
  • 能够容忍数据不是实时的

版本

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

LocalCache参数说明

类型参数名默认值说明
static final intMAXIMUM_CAPACITY1 << 30最大容量,如果其中一个具有参数的构造函数隐式指定了更高的值,则使用该容量。必须是2的幂{2^30},以确保Entry可以使用整数进行索引
static final intMAX_SEGMENTS1 << 16允许的最大分段数,并发数,这里设置的2^16;用于绑定构造函数参数
static final intCONTAINS_VALUE_RETRIES3containsValue方法中的(未同步)重试次数
static final intDRAIN_THRESHOLD0x3F在更新缓存的最近顺序信息之前,每个段可以缓冲的缓存访问操作数。这用于通过记录读取的memento并延迟锁获取,直到超过阈值或发生突变,来避免锁争用
static final intDRAIN_MAX16单次清理运行中要排出的最大Entry数。这独立适用于清理队列和两个引用队列
static finalloggerLogger.getLogger(LocalCache.class.getName())java.util.logging.Logger日志
final intsegmentMask——用于索引分段的掩码值。密钥哈希码的高位用于选择段
final intsegmentShift——分段内索引的移位值。有助于防止最终位于同一段中的Entry也位于同一桶中
final Segment<K, V>[]segments——每个段都是一个继承了ReentrantLock的哈希表
final intconcurrencyLevel——并发级别
final Equivalence<Object>keyEquivalence——键比较策略
final Equivalence<Object>valueEquivalence——值比较策略
final StrengthkeyStrength——键策略
final StrengthvalueStrength——值策略
final longmaxWeight——此Map的最大容量。如果没有最大值,则返回UNSET_INT
final Weigher<K, V>weigher——缓存容量计算器
final longexpireAfterAccessNanos——在Entry最后一次访问的多久内Map保留该Entry
final longexpireAfterWriteNanos——在Entry最后一次写入的多久内Map保留该Entry
final longrefreshNanos——当Entry上次写入多久后,Entry成为刷新的候选项。设置自动刷新间隔的
final Queue<RemovalNotification<K, V>>removalNotificationQueue——存放removalListener占用的Entry的等待队列
final RemovalListener<K, V>removalListener——当Entry因软/弱Entry的过期或垃圾回收而被删除时调用的监听器
final Tickerticker——com.google.common.base.Ticker计数器,用于计算时间
final EntryFactoryentryFactory——用于创建新Entry
final StatsCounterglobalStatsCounter——全局缓存数。请注意,还有每个分段的实体数量,必须聚合这些分段中的Entry数量才能获得全局总数
final CacheLoader<? super K, V>defaultLoader——加载操作时使用的默认缓存加载器

Cache构建过程

guava cache 最常用的方式是通过 CacheBuilder 进行构建, CacheBuilder 通过 build 方法返回一个new LocalCache.LocalLoadingCache<K1, V1>(this, loader);其中 LocalLoadingCache 继承了 LocalManualCache,而 LocalManualCache 定义了一个 final LocalCache<K, V>。

在这里插入图片描述
最终返回的LocalManualCache的所有操作均为对LocalCache的操作。

LocalCache介绍

LocalCache继承了AbstractMap并实现了ConcurrentMap。
LocalCache基本策略是对Entry分段存储,每个Segment本身都是一个并发可读的哈希表。该映射支持跨不同段的非阻塞读取和并发写入。如果指定了最大大小,则使用页面替换算法对段内的Entry进行替换。

LocalCache实例化

将builder中的属性赋值到LocalCache中

LocalCache(CacheBuilder<? super K, ? super V> builder, @CheckForNull CacheLoader<? super K, V> loader) {//从builder和默认(2^16)中选的最小的作为并发级别concurrencyLevel = Math.min(builder.getConcurrencyLevel(), MAX_SEGMENTS);//键值策略keyStrength = builder.getKeyStrength();valueStrength = builder.getValueStrength();//键值比较策略keyEquivalence = builder.getKeyEquivalence();valueEquivalence = builder.getValueEquivalence();//最大容量maxWeight = builder.getMaximumWeight();//容量计算器weigher = builder.getWeigher();//访问保留时间expireAfterAccessNanos = builder.getExpireAfterAccessNanos();//写入保留时间expireAfterWriteNanos = builder.getExpireAfterWriteNanos();//自动刷新间隔refreshNanos = builder.getRefreshNanos();//监听Entry被GC回收的监听器removalListener = builder.getRemovalListener();//removalListener的Entry队列,这里如果removalListener未设置,//则用的是discardingQueue,如果已设置则使用的是ConcurrentLinkedQueueremovalNotificationQueue = (removalListener == NullListener.INSTANCE) ? LocalCache.discardingQueue()  : new ConcurrentLinkedQueue<>();//计数器ticker = builder.getTicker(recordsTime());//实体工厂entryFactory = EntryFactory.getFactory(keyStrength, usesAccessEntries(), usesWriteEntries());//全局缓存数globalStatsCounter = builder.getStatsCounterSupplier().get();//默认加载器defaultLoader = loader;//设置初始容量,在builder的初始容量和2^30选更小的一个int initialCapacity = Math.min(builder.getInitialCapacity(), MAXIMUM_CAPACITY);//如果Map的最大容量>0且weigher是OneWeigher的实例if (evictsBySize() && !customWeigher()) {//初始容量是当前值与maxWeight之间更小的一个initialCapacity = (int) Math.min(initialCapacity, maxWeight);}……
}

其中evictsBySize方法和customWeigher方法如下:

    boolean evictsBySize() {return maxWeight >= 0;}boolean customWeigher() {return weigher != OneWeigher.INSTANCE;}

分段

  1. 计算段个数,如果未指定最大容量时,段个数=超过并发级别的最小2次幂,指定了最大容量时,段个数=超过并发级别和最大容量/20之间较小的值的最小2次幂。
        // 除非指定了maximumSize/Weight(在这种情况下,请确保每个段至少有10个Entry),// 否则找到超过并发级别的最小2次幂的段数量,然后对该段中的Entry进行移除,// 因为移除是按段而不是全局进行的,因此与最大大小相比,过多的段将导致随机移除行为。int segmentShift = 0;int segmentCount = 1;//未指定最大容量时,segmentCount=超过concurrencyLevel的最小2次幂//指定了最大容量时,segmentCount=超过Math.Min(concurrencyLevel,maxWeight/20)的最小2次幂while (segmentCount < concurrencyLevel&& (!evictsBySize() || segmentCount * 20L <= maxWeight)) {++segmentShift;segmentCount <<= 1;}this.segmentShift = 32 - segmentShift;segmentMask = segmentCount - 1;
  1. 通过newSegmentArray()创建段
        this.segments = newSegmentArray(segmentCount);
  1. 计算段容量,cache的段容量由两个变量控制,segmentSize为初始容量,maxSegmentWeight为能够扩容的最大容量。
        //段容量=Math.Ceiling(总容量/段数)int segmentCapacity = initialCapacity / segmentCount;if (segmentCapacity * segmentCount < initialCapacity) {++segmentCapacity;}//segmentSize=超过segmentCapacity的最小2次幂int segmentSize = 1;while (segmentSize < segmentCapacity) {segmentSize <<= 1;}//如果设置了Map的最大容量if (evictsBySize()) {//确保分段最大容量之和=总最大容量long maxSegmentWeight = maxWeight / segmentCount + 1;long remainder = maxWeight % segmentCount;for (int i = 0; i < this.segments.length; ++i) {if (i == remainder) {maxSegmentWeight--;}this.segments[i] =createSegment(segmentSize, maxSegmentWeight, builder.getStatsCounterSupplier().get());}} else {//没有设置最大容量,则每段大小为UNSET_INTfor (int i = 0; i < this.segments.length; ++i) {this.segments[i] =createSegment(segmentSize, UNSET_INT, builder.getStatsCounterSupplier().get());}}

初始容量计算比较简单,为超过Math.Ceiling(总容量/段数)的最小2次幂。如果设置了Map的最大容量,最大段容量计算如下图所示:
最大段容量计算
假设分段最大容量为25,段数量为7,则通过公式计算得出maxSegmentWeight=4,remainder=4。根据for循环,当下标为4时,maxSegmentWeight=maxSegmentWeight-1,则前4个段最大容量为4,而后三个段的最大容量为3。如此,确保了分段最大容量之和=总最大容量。
当没有设置最大容量,则每段大小为UNSET_INT。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/45785.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法复杂度<数据结构 C版>

什么是算法复杂度&#xff1f; 简单来说算法复杂度是用来衡量一个算法的优劣的&#xff0c;一个程序在运行时&#xff0c;对运行时间和运行空间有要求&#xff0c;即时间复杂度和空间复杂度。 目录 什么是算法复杂度&#xff1f; 大O的渐近表达式 时间复杂度示例 空间复杂度…

Jenkins 离线升级

1. 环境说明 环境 A: jenkins 版本&#xff1a;2.253使用 systemctl 管理的 jenkins 服务 环境 B&#xff1a; 可以上网的机器&#xff0c;装有 docker-compose docker 和 docker-compose 安装&#xff0c;这里都略了。 2. 安装旧版本 2.1 环境 A jenkins 目录打包文件 …

2024辽宁省大学数学建模竞赛试题思路

A题 (1) 建立模型分析低空顺风风切变对起飞和降落的影响 模型假设 飞机被视为质点&#xff0c;忽略其尺寸和形状对风阻的影响。风切变仅考虑顺风方向的变化&#xff0c;忽略其他方向的风切变。飞机的飞行速度、高度和姿态&#xff08;如迎角、俯仰角&#xff09;是变化的&am…

(补充):java各种进制、原码、反码、补码和文本、图像、音频在计算机中的存储方式

文章目录 前言一、进制1 逢几进一2 常见进制在java中的表示3 进制中的转换(1)任意进制转十进制(2)十进制转其他进制二、计算机中的存储1 计算机的存储规则(文本数据)(1)ASCII码表(2)编码规则的发展演化2 计算机的存储规则(图片数据)(1)分辨率、像素(2)黑白图与灰度…

服务器操作集合

服务器使用PC作为代理访问外网 1、PC上启动代理&#xff0c;比如nginx 下载nginx&#xff1a;http://nginx.org/en/download.html 修改配置文件&#xff0c;在conf下&#xff1a; http {include mime.types;default_type application/octet-stream;sendfile o…

罗技K380无线键盘及鼠标:智慧互联,一触即通

目录 1. 背景2. K380无线键盘连接电脑2.1 键盘准备工作2.2 电脑配置键盘的连接 3. 无线鼠标的连接3.1 鼠标准备工作3.2 电脑配置鼠标的连接 1. 背景 有一阵子经常使用 ipad&#xff0c;但是对于我这个习惯于键盘打字的人来说&#xff0c;慢慢在 ipad 上打字&#xff0c;实在是…

conda install问题记录

最近想用代码处理sar数据&#xff0c;解放双手。 看重了isce这个处理平台&#xff0c;在安装包的时候遇到了一些问题。 这一步持续了非常久&#xff0c;然后我就果断ctrlc了 后面再次进行尝试&#xff0c;出现一大串报错&#xff0c;不知道是不是依赖项的问题 后面看到说mam…

模块化(一)nodejs

模块化 一.模块化的基本概念1.1 什么是模块化1.2 模块化规范 二.Node.js 中的模块化2.1 Node.js 中模块的分类2.2 加载模块2.3 Node.js 中的模块作用域2.4 向外共享模块作用域中的成员 一.模块化的基本概念 1.1 什么是模块化 模块化 是指解决一个 复杂问题 时&#xff0c;自顶…

从Centos7升级到Rocky linux 9后,网卡连接显示‘Wired connection 1‘问题解决方法

问题描述 从Centos7升级到Rocky9后, 发现网卡eth0的IP不正确。通过nmcli查看网卡连接&#xff0c;找不到name为eth0的连接&#xff0c;只显示’Wired connection 1’ 查看/etc/NetworkManager/system-connections/&#xff0c;发现找不到网卡配置文件。 原因分析 centos7使…

华为OD机试 - 堆内存申请(Java 2024 D卷 100分)

华为OD机试 2024D卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;D卷C卷A卷B卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测…

Hadoop-28 ZooKeeper集群 ZNode简介概念和测试 数据结构与监听机制 持久性节点 持久顺序节点 事务ID Watcher机制

章节内容 上节我们完成了&#xff1a; ZooKeeper 集群配置ZooKeeper 集群启动ZooKeeper 集群状况查看Follower 和 Leader 节点 背景介绍 这里是三台公网云服务器&#xff0c;每台 2C4G&#xff0c;搭建一个Hadoop的学习环境&#xff0c;供我学习。 之前已经在 VM 虚拟机上搭…

基于SpringBoot+Vue的数码论坛系统(带1w+文档)

基于SpringBootVue的数码论坛系统(带1w文档) 基于SpringBootVue的数码论坛系统(带1w文档) 数码论坛系统能够通过互联网得到广泛的、全面的宣传&#xff0c;让尽可能多的用户了解和熟知数码论坛系统的便捷高效&#xff0c;不仅为用户提供了服务&#xff0c;而且也推广了自己&…

【pytorch】手写数字识别

https://blog.csdn.net/qq_45588019/article/details/120935828 基本均参考该博客 《深度学习原理Pytorch实战》 初步处理 导包 import torch import numpy as np from matplotlib import pyplot as plt from torch.utils.data import DataLoader from torchvision import tr…

Cypress UI自动化之安装环境

注&#xff1a;macOS系统 一、git环境 略 二、node环境 1、安装nvm 前提&#xff1a;有装过Homebrew&#xff0c;参考adb使用方法文档 1、安装nvm&#xff1a;首先要保证之前没有安装过node&#xff0c;如果之前安装过&#xff0c;先 brew uninstall node brew install n…

计算云服务4

第四章 弹性伸缩服务 什么是弹性伸缩(AS) 弹性伸缩(Auto Scaling&#xff0c;AS)是根据用户的业务需求&#xff0c;通过策略自动调整其业务资源的服务。用户可以根据业务需求自行定义伸缩配置和伸缩策略&#xff0c;降低人为反复调整资源以应对业务变化和高峰压力的工作量&am…

C语言 | Leetcode C语言题解之第231题2的幂

题目&#xff1a; 题解&#xff1a; const int BIG 1 << 30;bool isPowerOfTwo(int n) {return n > 0 && BIG % n 0; }

为什么远程办公不被公司普遍接受?

为什么远程办公不被公司普遍接受? 如果说传统企业不接受远程公办,那么为什么互联网企业也不接受远程办公呢? VPN 虚拟专用网络(Virtual Private Network) VPN 是远程办公的基石,没有VPN远程办公处处受限。什么是VPN呢? 它能实现跨地域,不受地域限制地把办公室网络和个…

暑期备考美国数学竞赛AMC8和AMC10:吃透1850道真题和知识点

距离接下来的AMC8、AMC10美国数学竞赛还有几个月的时间&#xff0c;实践证明&#xff0c;做真题&#xff0c;吃透真题和背后的知识点是备考AMC8、AMC10有效的方法之一。 通过做真题&#xff0c;可以帮助孩子找到真实竞赛的感觉&#xff0c;而且更加贴近比赛的内容&#xff0c;…

全面升级的对象创建——抽象工厂模式(Python实现和JAVA实现)

1. 引言 大家好&#xff01;在之前的文章中&#xff0c;我们探讨了简单工厂和工厂方法模式&#xff1a; 轻松创建对象——简单工厂模式&#xff08;Python实现&#xff09; 轻松创建对象——简单工厂模式&#xff08;Java实现&#xff09; 灵活多变的对象创建——工厂方法模式…

12-《向日葵》

向日葵 向日葵&#xff08;拉丁文&#xff1a;Helianthus annuusL.&#xff09;&#xff0c;为木兰纲、菊目、菊科、向日葵属的一年生草本植物。高1&#xff5e;3.5米。茎直立&#xff0c;圆形多棱角&#xff0c;质硬被白色粗硬毛。广卵形的叶片通常互生&#xff0c;先端锐突或…