首页 > 其他分享 >聊一聊Integer的缓存机制问题

聊一聊Integer的缓存机制问题

时间:2024-03-06 10:34:27浏览次数:20  
标签:缓存 int high 对象 聊一聊 127 Integer

在Java编程中,Integer类作为基本类型int的包装器,提供了对象化的操作和自动装箱与拆箱的功能。从JDK5开始引入了一项特别的优化措施——Integer缓存机制,它对于提升程序性能和减少内存消耗具有重要意义。接下来我们由一段代码去打开Integer缓存机制的秘密。

public static void main(String[] args) {  
    Integer i1 = 100;  
    Integer i2 = 100;  
    System.out.println(i1 == i2);  
    Integer i3 = 1000;  
    Integer i4 = 1000;  
    System.out.println(i3 == i4);  
}

至于答案是什么呢?我们接着往下看,等你看完就明白了。

当你在你的Idea中写出这段代码的时候,Idea就会提示你要使用equals()方法区比较大小,因为Integer是对象,对象的值比较要用equals()方法,而不是使用==,这里我们主要是研究一下Integer的缓存机制。

Integer缓存是什么

Java的Integer类内部实现了一个静态缓存池,用于存储特定范围内的整数值对应的Integer对象。默认情况下,这个范围是-128至127。当通过Integer.valueOf(int)方法创建一个在这个范围内的整数对象时,并不会每次都生成新的对象实例,而是复用缓存中的现有对象。我们看一下Integer.valueOf(int)的源码:

@HotSpotIntrinsicCandidate  
public static Integer valueOf(int i) {  
    if (i >= IntegerCache.low && i <= IntegerCache.high)  
        return IntegerCache.cache[i + (-IntegerCache.low)];  
    return new Integer(i);  
}

对于Integer.valueOf(int)方法来说,由于这个方法经常用于将基本类型int转换为包装器对象,所以它使用了@HotSpotIntrinsicCandidate注解,这样HotSpot JVM可能会提供一种更为高效的内部实现来处理自动装箱操作。而IntegerCacheInteger内部的一个静态类,负责缓存整数对象。它在类加载时被初始化,创建并缓存范围内的所有整数对象。我们看一下IntegerCache的源码:

private static class IntegerCache {  
    // 缓存范围的下限,默认为-128  
    static final int low = -128;  
    // 缓存范围的上限,初始化时动态计算(基于系统属性或默认值127)  
    static final int high;  
    // 存储在缓存范围内所有Integer对象的数组  
    static final Integer cache[];  
    // 静态初始化块,在类加载时执行  
    static {  
        // 初始设定high为127  
        int h = 127;  
        // 尝试从系统属性获取用户自定义的最大整数值  
        String integerCacheHighPropValue =  
                VM.getSavedProperty("java.lang.Integer.IntegerCache.high");  
        // 如果系统属性存在并且可以转换为int类型,则更新high值  
        if (integerCacheHighPropValue != null) {  
            try {  
                int i = parseInt(integerCacheHighPropValue);  
                // 确保high至少为127,并且不超过Integer.MAX_VALUE允许的最大数组大小  
                h = Math.max(i, 127);  
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);  
            } catch( NumberFormatException nfe) {  
            }  
        }  
  
        // 设置最终确定的high值  
        high = h;  
        // 初始化cache数组,长度等于缓存范围内的整数数量  
        cache = new Integer[(high - low) + 1];  
        // 使用循环填充cache数组,创建并存储对应的Integer对象  
        int j = low;  
        for(int k = 0; k < cache.length; k++) {  
            cache[k] = new Integer(j++);  
        }  
        // 检查,确保缓存范围至少包含[-128, 127]  
        // 这是Java语言规范对小整数自动装箱共享的要求  
        assert IntegerCache.high >= 127;  
    }  
    // 私有构造器,防止外部实例化此内部类的对象  
    private IntegerCache() {}  
}

IntegerCache类在Java虚拟机启动时创建了一个固定大小的数组,用于缓存指定范围内所有的Integer对象。这样在后续程序运行过程中,对于这些范围内的整数进行装箱操作时,可以直接从缓存中获取已存在的对象,以提升性能并减少内存开销。同时,它也提供了根据系统属性(-Djava.lang.Integer.IntegerCache.high)来自定义缓存上限的能力,并确保满足Java语言规范关于小整数自动装箱共享的规定。

Integer.value(int)方法中,如果int的值在IntegerCache返回的lowhigh之内,则直接返回IntegerCache中缓存的对象,否则重新new一个新的Integer对象。

而文章开头示例中,我们使用Interge i1 = 100的方式其实是Java的自动装箱机制,整数字面量100是一个基本类型的int值。当赋值给一个Integer引用变量i时,编译器会隐式地调用Integer.valueOf(int)方法将这个基本类型的int值转换为Integer对象。

整数在编程中经常被使用,特别是在循环计数等场景中,通过缓存整数对象,可以大幅度减少相同整数值的对象创建,从而减小内存占用。

由此我们可以看出因为100在[-128, 127]之内,所以i1 == i2打印true,而1000不在[-128, 127]之内,所以i3 == i4打印false
image.png

我们尝试使用java.lang.Integer.IntegerCache.high调整一下high为1000,然后看一下效果:
image.png
image.png
打印结果都是true。

当然这个上限不要随意去调整,调整之前,需要仔细评估应用程序的实际需求和性能影响。尽量选择在[-128, 127]范围内的整数值,以充分利用Integer缓存机制。

注意事项

  • 比较: 由于缓存的存在,在-128至127之间的Integer对象在进行==运算符比较时,结果可能是true,因为它们指向的是同一个内存地址。而在缓存范围之外创建的Integer对象即使值相等,也会视为不同的对象,因此使用==比较会返回false。不论是否启用缓存,对于任何两个Integer对象,只要其包含的整数值相同,调用equals()方法始终会返回true。所以我们在比较对象时一定要使用equals()方法。

  • 不适用于所有场景: 当使用new Integer(i)直接创建Integer对象时,不会利用缓存。

  • 不要随意去扩展缓存的上下限

总结

Integer缓存机制是Java中的一项性能优化措施,通过缓存一定范围内的整数对象,既能减小内存开销,又能提高性能。

本文已收录于我的个人博客:码农Academy的博客,专注分享Java技术干货,包括Java基础、Spring Boot、Spring Cloud、Mysql、Redis、Elasticsearch、中间件、架构设计、面试题、程序员攻略等

标签:缓存,int,high,对象,聊一聊,127,Integer
From: https://www.cnblogs.com/coderacademy/p/18044486

相关文章

  • mybatis面试高频问题---执行流程/延迟加载/缓存
    mybatis一.mybatis执行流程理解了各个组件的关系Sql的执行过程(参数映射、sql解析、执行和结果处理)二.mybatis支持延迟加载1.立即加载查询用户信息的同时也可以查询到相关订单信息UserMapper:OrderMapper:UserTest.java打印输出用户信息执行结果:2.延迟加载f......
  • 【HarmonyOS】聊一聊ArkUI中的Image组件缓存
    【关键字】image组件,缓存 【问题描述】给Image组件设置网络图片展示,业务将图片进行了下线处理,再次进入这个页面Image组件还是展示着这张图,而不是恢复了默认占位图。 【问题分析】这个问题显然是跟Image组件的缓存机制有关。目前Image组件的图片会有内存缓存机制,会统计到......
  • 浏览器 缓存
    2种方法解决图片缓存:方法1:添加代码https://www.cnblogs.com/lgx5/p/15180516.html 方法2:浏览器里面勾上不要缓存。https://juejin.cn/post/7178794675044614203  erwa.cn二娃备忘 ......
  • Python变量的缓存机制
    当然,我会帮你整理这些Python代码并转化为Markdown格式的笔记。以下是你的Markdown笔记:Python变量的缓存机制在Python3.6版本中,对于某些类型的变量,如果它们的值相同,那么它们在内存中的地址(即它们的id)也可能相同。这种现象称为变量的缓存机制。1.整型对于整型而言,-5到正无穷......
  • 聊一聊日常开发中如何优雅的避免那无处不在的空指针异常
    在Java编程语言中,NullPointerException(简称NPE)是一种常见的运行时异常,当程序试图访问或操作一个还未初始化(即值为null)的对象引用时,Java虚拟机就会抛出NullPointerException。如果我们在日常开发中,不能很好的去规避NPE,那么可能因为数据或者其他问题就会导致线上问题。。。很烦。。......
  • 面试必备:一线大厂Redis缓存设计规范与性能优化
    说在前面你是否在使用Redis时,不清楚Redis应该遵循的设计规范而苦恼?你是否在Redis出现性能问题时,不知道该如何优化而发愁?你是否被面试官拷问过Redis的设计规范和性能优化而回答不出来别慌,看这篇文章就行了本文,已收录于,我的技术网站aijiangsir.com,有大厂完整面经,工作技术,架构......
  • Vue Router系列之(十)缓存路由组件
    缓存路由组件完善路由的技巧作用:让不展示的路由组件保持挂载,不被销毁。​ 正常情况下,进行了路由跳转后,之前的组件会被销毁,如果之前的组件中存在input框之类的表单类组件,输入的内容也就消失了,也就是说,要保证路由跳转后不销毁之前的组件具体编码://缓存的对象:并不是所有路......
  • bat合并下载缓存文件
    ::进入当前bat文件所在目录cd%cd%@echooffsetlocalENABLEDELAYEDEXPANSION::设置数组obj的值setobjLentth=2setobj[0]=test1setobj[1]=test2@echo!obj[0]!@echo!obj[1]!setobjIndex=0::===在这里设置你的后缀名sethouzhui=.jpg.gif.png.m4s::===......
  • 缓存一致性协议MESI
    从Cache说起如今的多核计算机往往包含多个CPU核心,其中每个CPU有自己独立的CacheL1、CacheL2,同时多个CPU共享主存。由于CPU获取指令数据的速度远快于主存,所以通过一级缓存、二级缓存等等来降低CPU从主存获取数据的频率,提升性能。CPU不会每需要一次数据就从主......
  • SpringBoot 1x 系列之(八)Spring Boot与缓存
    SpringBoot与缓存JSR-107、Spring缓存抽象、整合Redis缓存:加速系统访问,提升系统性能热点数据、临时数据(如验证码)1.JSR-1071.1背景统一缓存的开发规范及提升系统的扩展性,J2EE发布了JSR-107缓存规范1.2JSR107简介CacheManager与Cache的关系,类比连接池与连接涉及的包ja......