Guava LocalCache源码分析:LocalCache生成

news2024/9/23 5:16:00

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,如果已设置则使用的是ConcurrentLinkedQueue
        removalNotificationQueue = (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_INT
            for (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.coloradmin.cn/o/1922602.html

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

相关文章

Spring MVC入门3

看完这篇博客你能学到什么 理解JSON的使用理解注解PathVariable理解解注解RequestPart理解cookie和Session的基本概念理解cookie和Session的区别 如果想真正掌握&#xff0c;还需要自己勤加练习。 正文 JSON JSON概念 JSON&#xff1a;JavaScript Object Notation 【JavaS…

免费长效IP在业务场景中的深度应用解析

在数字化竞赛的跑道上&#xff0c;每一秒的都至关重要&#xff0c;而免费长效IP&#xff0c;不仅为企业减少了运营成本&#xff0c;更在数据安全与访问效率上筑起了一道坚实的保护线。然而&#xff0c;面对市场上琳琅满目的代理服务&#xff0c;如何挑选出能应对各种业务场景的…

【JAVA poi-tl-ext 富文本转word】

富文本转word 环境使用poi-tl-ext的原因富文本转word代码 环境 jdk 1.8 <dependency><groupId>io.github.draco1023</groupId><artifactId>poi-tl-ext</artifactId><version>0.4.16</version> </dependency>poi-tl-ext已经包…

ES 慢上游响应问题优化在用户体验场景中的实践

在抖音亿级日活流量的情况下&#xff0c;每天收到的用户反馈也是大量的&#xff0c;而用户反馈对于产品的发展与未来是至关重要的&#xff0c;因此用户体验管理平台&#xff08;简称VoC&#xff09;就应运而生&#xff0c;VoC 平台旨在通过技术平台化的方式&#xff0c;结合反馈…

字体反爬之自动化通过字体文件生成映射字典

1、首先找到以.ttf结尾的字体文件&#xff0c;下载下来&#xff0c;以我的字体文件sfont.ttf为例 sont.ttf下载地址https://download.csdn.net/download/lingyingdon/89534953 目前只测试了.ttf文件。如果想使用woff字体文件&#xff0c;请自行测试 2、下载分割字体文件的软件…

从汇编层看64位程序运行——参数传递的底层实现

大纲 小于等于6个参数一个参数总结 两个参数总结 三个参数总结 四个参数总结 五个参数总结 六个参数总结 大于6个参数七个参数总结 在32位系统中&#xff0c;参数的传递主要依靠栈来进行。那么64位系统上&#xff0c;是否依旧符合这个规则呢&#xff1f;答案是“不是”。64位系…

Objective-C 自定义渐变色Slider

文章目录 一、前情概要二、具体实现 一、前情概要 系统提供UISlider&#xff0c;但在开发过程中经常需要自定义&#xff0c;本次需求内容是实现一个拥有渐变色的滑动条&#xff0c;且渐变色随着手指touch的位置不同改变区域&#xff0c;类似如下 可以使用CAGradientLayer实现渐…

【Linux】Linux操作系统

Linux基本指令 os概念与定位 本节内容&#xff1a; Linux操作系统讲解 os概念与定位 操作系统&#xff08;Operating System&#xff0c;简称OS&#xff09;是管理和控制计算机硬件与软件资源的计算机程序。总的来讲&#xff0c;操作系统是一款做软硬件管理的软件。 了解操作…

springBoot(若依)集成camunda

1、下图为项目结构 2、最外层 pom引入依赖 <properties><!--camunda 标明版本&#xff0c;注意要个自己的Spring 版本匹配&#xff0c;匹配关系自行查询官网--><camunda.version>7.18.0</camunda.version> </properties> 3、common模块引入依赖 …

安卓14中Zygote初始化流程及源码分析

文章目录 日志抓取结合日志与源码分析systemServer zygote创建时序图一般应用 zygote 创建时序图向 zygote socket 发送数据时序图 本文首发地址 https://h89.cn/archives/298.html 最新更新地址 https://gitee.com/chenjim/chenjimblog 本文主要结合日志和代码看安卓 14 中 Zy…

抗量子密码算法:保障未来信息安全的新盾牌

随着量子计算的迅猛发展&#xff0c;传统加密算法正面临着前所未有的挑战。量子计算机利用量子比特的特殊性质&#xff0c;能在极短时间内破解目前广泛使用的公钥加密体系&#xff0c;如RSA、ECC等。这使得我国及全球的信息安全体系遭受严重威胁。为了应对这一挑战&#xff0c;…

知识图谱入门笔记

自学参考&#xff1a; 视频&#xff1a;斯坦福CS520 | 知识图谱 最全知识图谱综述 详解知识图谱的构建全流程 知识图谱构建&#xff08;概念&#xff0c;工具&#xff0c;实例调研&#xff09; 一、基本概念 知识图谱&#xff08;Knowledge graph&#xff09;&#xff1a;由结…

基于LSTM的局部特征提取网络算法原理

目录 一、LSTM的基本原理与结构 1. LSTM的核心结构 2. LSTM的工作原理 二、基于LSTM的局部特征提取 1. 输入处理与序列表示 2. LSTM层处理与特征提取 3. 特征提取的优势与应用 三、实现细节与注意事项 1. 数据预处理 2. 网络结构与参数选择 3. 训练策略与正则化 4.…

Chapter 1:数据结构前言

在数字化的世界里&#xff0c;我们每天都在与数据打交道。然而&#xff0c;你是否曾想过&#xff0c;这些数据是如何被组织、存储和处理的&#xff1f;这就是数据结构的魅力所在。 数据结构&#xff0c;简单来说&#xff0c;就是数据的组织方式。它决定了我们如何高效地访问和操…

【游戏客户端】大话slg玩法架构(三)建筑控件

【游戏客户端】大话slg玩法架构&#xff08;三&#xff09;建筑控件 大家好&#xff0c;我是Lampard家杰~~ 今天我们继续给大家分享SLG玩法的实现架构&#xff0c;关于SLG玩法的介绍可以参考这篇上一篇文章&#xff1a;【游戏客户端】制作率土之滨Like玩法 PS&#xff1a;和之前…

JVM系列 | 垃圾收集算法

JVM系列 | 垃圾收集算法 文章目录 前言如何判断对象已"死"&#xff1f;引用计数法可达性分析算法可达性分析2.0版 | 引用的增强对象的消亡过程回收方法区主要回收目标&#xff1a;回收操作 垃圾收集算法分代收集理论 与 跨代引用假说分代收集理论跨带引用假说 垃圾收…

Oracle数据库加密与安全

Wallet简介&#xff1a; Oracle Wallet(即内部加密技术TDE( Transparent DataEncryption&#xff09; TDE是 Oracle10gR2中推出的一个新功能,使用时要保证Oracle版本是在10gR2或者以上 Wallet配置&#xff1a; 1.创建一个新目录&#xff0c;并指定为Wallet目录 /home/oracle…

论文翻译:Large Language Models for Education: A Survey and Outlook

https://arxiv.org/abs/2403.18105 目录 教育领域的大型语言模型&#xff1a;一项调查和展望摘要1. 引言2. 教育应用中的LLM2.1 概述2.2 学习辅助2.2.1 问题解决&#xff08;QS&#xff09; 2.2.2 错误纠正&#xff08;EC&#xff09;2.2.3 困惑助手&#xff08;CH&#xff09;…

ExcelToDB2:批量导入Excel到IBM DB2数据库的自动化工具

ExcelToDB2&#xff1a;批量导入Excel到IBM DB2数据库的自动化工具 简介 ExcelToDB2是一个可以批量导入Excel到IBM DB2数据库的自动化工具。支持将xls/xlsx/xlsm/xlsb/csv/txt/xml格式的Excel文件导入到IBM DB2等多种原生及国产数据库。自动化是其最大的特点&#xff0c;因为它…

Python爬虫教程第5篇-使用BeautifulSoup查找html元素几种常用方法

文章目录 简介find()和find_all()字符串通过id查找通过属性查找通过.方式查找通过CSS选择器查找通过xpath查找正则表达自定义方法总结 简介 上一篇详细的介绍了如何使用Beautiful Soup的使用方法&#xff0c;但是最常用的还是如何解析html元素&#xff0c;这里再汇总介绍下查询…