源码分析Integer a = 100的这种直接赋值操作,是调⽤Integer.valueOf(100)方法,从Integer.valueOf()源码可以看到,返回的是Integer对象,但这里的实现并不是简单的 扩展:Byte,Short,Integer,Long 这 4 种包装类默认创建了数值 -128,127 的相应类型的缓存数据,Character 创建了数值在 0,127 范围的缓存数据,Boolean ,缓存范围是 -128,127,1、2都在范围内,返回的是缓存中的对象,因此输出true,3不在范围内,返回的是新 new 的Integer,因此输出false。 128 在缓存范围外,按道理会 new 出一个Integer对象,为什么输出true呢? a * b : c);思考缓存机制存在的原因:将频繁被使用的对象缓存起来,可以提升读取的效率,这是一个典型的用空间换时间的例子(其实缓存机制都是这个原理),而Java开发者认为-128,127是比较常使用的范围
❝相信很多人都知道,Integer的值范围为-128到127,超过就会创建一个新的Integer对象,其实这里的值范围在jdk源码中是指缓存的值范围 ❞ 那么为什么会缓存这一段区间的数值? 缓存的区间可以修改吗?其它的包装类型有没有类似缓存? 1、源码分析 我们知道, Integer var = ? :(I)Ljava/lang/Integer; 5: astore_1 6: bipush 99 8: invokestatic #2 // Method java/lang/Integer.valueOf :(I)Ljava/lang/Integer; 表示调用一个 static 函 数,即 java.lang.Integer#valueOf(int) ; 偏移为 5 的指令为:astore_1 ,其含义是从操作数栈中弹出对象引用 long 64bit Long -128~127 否 float 32bit Float / / double 64bit Double / / 5、总结 ❝我们通过源码分析和反编译进行Integer
本文将介绍 Java 中 Integer 缓存的相关知识。这是 Java 5 中引入的一个有助于节省内存、提高性能的特性。 Java 中 Integer 缓存实现 在 Java 5 中,为 Integer 的操作引入了一个新的特性,用来节省内存和提高性能。整型对象在内部实现中通过使用相同的对象引用实现了缓存和重用。 这种 Integer 缓存策略仅在自动装箱(autoboxing)的时候有用,使用构造器创建的 Integer 对象不能被缓存。 以后,就可以使用缓存中包含的实例对象,而不是创建一个新的实例(在自动装箱的情况下)。 实际上在 Java 5 中引入这个特性的时候,范围是固定的 -128 至 +127。 其他缓存的对象 这种缓存行为不仅适用于Integer对象。我们针对所有整数类型的类都有类似的缓存机制。
Integer是一个封装类看一段代码 public static void main(String[] args) { Integer a = 127; Integer :(I)Ljava/lang/Integer; 5: astore_1 6: bipush 127 8: invokestatic #7 15: invokestatic #7 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer (i); }数组缓存的最低最高可以看到系统配置的属性,最高会和127做比较否则127就是最高调试代码可以看到到了127地址都不一样了看看cache的缓存内容从-128到127片段2看到初始化,这下子明白了吗 类缓存初始化 /** * Cache to support the object identity semantics of autoboxing for values between
Java Integer包装类缓存机制详解问题引入在解决力扣第76题「最小覆盖子串」时,我使用了Map<Character, Integer>来记录字符串中各字符的出现次数。 当包装类被加载到内存时,JVM会为其创建一个静态内部缓存类,该缓存保存在堆内存中。对于Integer类型,当数值在-128到127之间时,会直接使用缓存中的对象,此时==和equals()的效果相同。 重要区别:Integer.valueOf()方法:会使用缓存,对于-128到127范围内的数值,返回缓存中的对象Integer.parseInt()方法:不使用缓存,每次都会创建新的Integer对象示例代码 :Integer a = 127;Integer b = 127;System.out.println(a == b); // true,使用缓存Integer c = 128;Integer d = 128;System.out.println(c == d); // false,超出缓存范围Integer e = Integer.valueOf(100);Integer f = Integer.valueOf
根据代码结构可以看到IntegerCache是Integer中的一个静态类,有一个私有构造器,对外暴露了三个属性,还有一个静态代码块,主要做的事情就是从虚拟机层面获取要缓存的Integer的最大值,然后创建了一个可以理解为长度是 127-(-128) + 1的Integer数组,+1就是要缓存0,最后循环为数组赋值,最后cache[]中存放了-128~-1、0和1~127的数组。 由于静态代码块是在类加载完后和初始化之前执行,所以在调用Integer的任何方法(包含构造器)之前,IntegerCache完成了加载且Integer cache[]完成了初始化,调用方法的时候根据需要能够使用数组中的缓存值 再回到文章最开始的例子中,我们调用的Integer.value(1)和Integer.valueOf(127)其实都是从缓存中取得值,所以也就能够解释为什么a1==a2和b1 == b2了。 以上是对jdk源码中Integer缓存做了一些分析,希望对各位带来帮助,如果有觉得分析不到位或者理解有偏差的,可以直接留言或者私聊我。
问原因则随口就说”Integer缓存了-128到127之间的整数对象“,为什么会缓存?还有其他答案?可能就不知道了。 what??? 难道这不是标准答案?还想咋地? ,如果是-128到127之间的整数,则会使用整数缓存对象,否则就new一个整形对象。 :(I)Ljava/lang/Integer; 5: astore_1 6: bipush 100 8: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 11: astore_2 12: sipush <high> 来设置缓存范围的最大值(包含)。
问原因则随口就说”Integer缓存了-128到127之间的整数对象“,为什么会缓存?还有其他答案?可能就不知道了。 what??? 难道这不是标准答案?还想咋地? (i); } 通过源码和注释可以看到 如果是-128到127之间的整数,则会使用整数缓存对象,否则就new一个整形对象。 :(I)Ljava/lang/Integer; 5: astore_1 6: bipush 100 8: invokestatic #2 cache[]; // 省略 } 可以看到可以通过设置虚拟机参数:XX:AutoBoxCacheMax=或 -Djava.lang.Integer.IntegerCache.high= 来设置缓存范围的最大值 另外缓存是 《Java语言规范》的要求,具体可以去语言规范里看。 三、总结 我们看一些文章的时候,不要止步于文章给出的标准答案,否则总是“背”文章,印象不深刻。
关于 Integer 的值缓存 这涉及 Java 5 中另一个改进。构建 Integer 对象的传统方式是直接调用构造器,直接 new 一个对象。 但是根据实践,我们发现大部分数据操作都是集中在有限的、较小的数值范围,因而,在 Java 5 中新增了静态工厂方法 valueOf,在调用它的时候会利用一个缓存机制,带来了明显的性能改进。 按照 Javadoc,这个值默认缓存是 -128 到 127 之间。 那么Integer对象的大小是多少呢? Integer只有一个int类型的成员变量value,所以其对象实际数据部分的大小是4个字节,然后再在后面填充4个字节达到8字节的对齐,所以可以得出Integer对象的大小是16个字节。 因此,我们可以得出Integer对象的大小是原生的int类型的4倍。
2,如果使用int类型,注意int类型的初始值是0,很多设计某某状态时,很喜欢用0作为某个状态,这里要小心使用。 (num3.equals(num4)); System.out.println(num3 == num4); 面试题3 Integer num5 = 1000; Integer num6 = 1000 ; System.out.println(num5.equals(num6)); System.out.println(num5 == num6); 把上面看完了,再回头来看看这种面试题,还难吗? 4.Integer.valueOf()方式,比较是否在缓存范围之内,在就直接从缓存中获取,不在new一个Integer对象。 5.每次使用new来创建Integer对象,是用不到IntegerCache缓存的。 6.Integer中的使用缓存的方式也可以理解为享元模式。
Java中Integer的缓存实现 在Java 5中,对于Integer的操作引入了一个新功能来节省内存和提高性能。整型对象通过使用相同的对象引用实现了缓存和重用。 assert IntegerCache.high >= 127; } private IntegerCache() {} } 从上面两段代码可以看出,在自动装箱的情况下,缓存通过一个 这个缓存会在Integer类第一次被使用的时候初始化出来。 实际上这个功能在Java 5中引入的时候,范围是固定的-128 至 +127。后来在Java 6中,可以通过java.lang.Integer.IntegerCache.high设置最大值。 在程序中,第一次使用Integer的时候也需要一定的额外时间来初始化这个缓存。
它具有内存和磁盘存储,缓存加载器,缓存扩展,缓存异常处理程序,一个gzip缓存servlet过滤器,支持REST和SOAP api等特点。 Object queryUser(@PathVariable("id") Long id) { return this.userService.findById(id); } } 5. springboot2.0.5依赖的基础库是spring5.x,而spring5中已经取消了对guava缓存的支持。 1.添加caffeine依赖 <! spring.cache.caffeine.spec=initialCapacity=50,maximumSize=500,expireAfterWrite=100s,refreshAfterWrite=5s : @Configuration public class CaffeineConfiguration { /** * 必须要指定这个Bean,refreshAfterWrite=5s
跟Hibernate一样,Mybatis也有一级缓存、二级缓存,并预留了集成第三方的缓存接口。 除了基础缓存之外,MyBatis也定义了很多装饰器,同样实现了Cache接口,通过这些装饰器可以额外实现很多功能。 ? 所有缓存可以分为三大类:基本缓存、淘汰算法缓存、装饰器缓存。 策略缓存 当缓存达到上限时,删除最先入队的缓存 evication="FIFO" SoftCache \ WeakCache 带清理策略的缓存 通过JVM的软引用和弱引用来实现缓存,当JVM内存不足时 事务缓存 在二级缓存中使用,可一次存入多个缓存,移除多个缓存 在TransactionalCacheManager中用Map维护对应关系 一级缓存 一级缓存也叫本地缓存(Local Cache),MyBatis 而一级缓存是在SqlSession内部的,所以肯定是工作在一级缓存之前,也就是只有取不到二级缓存的情况下才到一个会话中去取一级缓存。 二级缓存是在哪里维护的呢?
究其原因,竟是因为Long类型的LongCache机制引起,且因为测试环境的模拟用户数据量较少,没有突破LongCache内部缓存数组的边界,导致该问题没能及时在测试环境发现。 Java在数据封装类型的设计中,都带了.equals()方法。 四、其他内容 1、Integer、Short、Character、Bytes等封装类也有类似的机制; 2、请关注JVM参数:AutoBoxCacheMax 3、请关注Integer内部类IntegerCache int1 = Integer.valueOf("100"); Integer int2 = Integer.valueOf("100"); System.out.println i = 0; i < cache.length; i++) cache[i] = new Long(i - 128); } } Long也做了缓存
概述 缓存设计需要关注的点 关注指标: KV大小 读写峰值 命中率 缓存空间大小 置换策略 穿透加载时间 分类 本地缓存 远程缓存 应用模式 Cache Aside Read/Write Through Write Back Caching 技巧/Tips 不要把缓存当数据库使用 评估业务所需缓存空间大小 缓存会给系统带来数据不一致的风险 尽量设置过期时间 缓存对象太大需要考虑做压缩,减少存储空间以及传输中带宽的占用 引入多级缓存时,需要考虑数据一致性 提前考虑扩容问题 常见的缓存问题 缓存雪崩 很多使用场景,查询的缓存数据都是由定时任务取刷新,然后缓存查不到从 DB 查了在更新缓存。 这种同一时间大量缓存的失效,导致请求直接打到 DB 上的情况, 就是缓存雪崩。 缓存穿透 缓存穿透指的是缓存缓存和数据库中都没有的数据,而用户不断发起请求,让请求直接落再DB上,增加数据库压力,可能导致数据库被击垮。这种请求很可能是攻击者恶意发起的。
缓存可以存在于体系结构中的所有级别,但通常位于最靠近前端的级别,在那里实现缓存可以快速返回数据,而不会对下游级别造成负担。 应用服务器缓存 将缓存直接放置在请求层节点上可以实现响应数据的本地存储。 image.png cache-aside(旁路缓存) 发生在应用层,应用层保证缓存结果同DB的数据一致性,应用层来负责写入到数据库和整理缓存,缓存层则不必插手此事。 因此也会带来缓存穿透、缓存雪崩、缓存击穿、缓存数据不一致等问题 refresh-ahead 简单的说就是在缓存数据过期前,能自动的刷新缓存数据。举个例子来说,某条数据在缓存中,过期时间是60秒。 当在48-60秒这个区间取数据时,缓存先将之前缓存的结果返回给外部应用程序,然后异步的再从数据库去更新缓存中的值,以尽可能的保证缓存的值是最新的。 5.最少使用频率(LFU):统计需要某个项目的频率。最不常用的会先被丢弃。 6.随机替换(RR):随机选择一个候选项,并在必要时丢弃它以腾出空间。
Write Back 套路就是,在更新数据的时候,只更新缓存,不更新数据库,而我们的缓存会异步地批量更新数据库。这个设计的好处就是让数据的 I/O 操作飞快无比(因为直接操作内存嘛)。 在软件设计上,我们基本上不可能做出一个没有缺陷的设计,就像算法设计中的时间换空间、空间换时间一个道理。有时候,强一致性和高性能,高可用和高性能是有冲突的。 image.png 缓存设计的重点 缓存更新的模式基本如前面所说,不过这还没完,缓存已经成为高并发高性能架构的一个关键组件了。现在,很多公司都在用 Redis 来搭建他们的缓存系统。 另外,缓存是通过牺牲强一致性来提高性能的,这世上任何事情都不是免费的,所以并不是所有的业务都适合用缓存,这需要在设计的时候仔细调研好需求。使用缓存提高性能,就是会有数据更新的延迟。 缓存数据的时间周期也需要好好设计,太长太短都不好,过期期限不宜太短,因为可能导致应用程序不断从数据存储检索数据并将其添加到缓存。
Openresty本身也支持开辟内存添加共享缓存的空间,操作api与redis一致 本地缓存的作用 减少查询redis、mysql的操作,实际redis也很快,但是毕竟有网络开销。 本地缓存会更快一些! so;;"; # ① 开启本地共享缓存,会占用内存:150mb 缓存名mycache,自定义 lua_shared_dict mycache 128m; # 在这里配置 也就是不会超时 ngx.say('缓存设定成功!') -- 读取数据 local name = getCache:get('name') ngx.say('第二次读取本地缓存!' ,name) -- 删除数据 --getCache:delete('name') -- 特别说明 -- 1、重载配置文件,缓存不会消失 -- 2、nginx重启才会删除缓存数据 特殊说明: 以上文章
1.sessionStorage <script type="text/javascript"> var txt = document.querySelector('input'); var btns = document.querySelectorAll('button'); //存储数据 btns[0].onclick=function(){ sessionStorage.setItem('userName',txt.value); ses
MyBatis 缓存装饰器 介绍完了 MyBatis 缓存的设计思想之后,我们一起来看几个比较有意思的缓存装饰器实现。 LruCache 首先我们来看下 LruCache。 在介绍完了 MyBatis 的缓存功能之后,最后我们来看一下缓存 Key 的设计。 那么 MyBatis 中的缓存 Key 是如何设计的呢? 小结 本篇详细介绍了 MyBatis 缓存模块的底层原理,包括缓存的基础实现、具备各种扩展功能的缓存装饰器,以及缓存 Key 的设计思想。 个人认为,缓存模块中的精髓就是装饰器设计模式的灵活运用,它使得用户在使用缓存时,可以根据不同的需求来灵活地定制化功能。这种设计思想非常值得我们借鉴。