Integer类缓存机制

问题:

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

实践:

java 复制代码
public class Test {
    public static void main(String[] args) {

        Integer a = Integer.valueOf(1);
        Integer b = Integer.valueOf(1);
        System.out.println(a==b);;

    }
}

源码:

java 复制代码
  private static class IntegerCache {
            //整体的思路就是 
            //1.valueof或者自动装箱调用该类的静态代码块
            //2.可以通过jvm设定最大值,也可以不设定
            //3.设定好最大值,计算范围,生成新的数组赋值给归档文件,
            //4.最后再赋值给cache缓存


        //缓存的下界,默认值为 -128,固定不可修改
        static final int low = -128;
        //缓存的上界,默认值为 127,但可以通过 JVM 参数动态调整
        //java -Djava.lang.Integer.IntegerCache.high
        static final int high;


        //缓存数组,存储从 low 到 high 范围
        //这个数组就是缓存机制的核心
        static final Integer[] cache;



        //一个额外的缓存数组,用于支持 Java 的 Class Data Sharing (CDS) 功能。
        //CDS 是一种优化技术,允许将共享的类数据(如缓存池)保存到归档文件中,从而加速 JVM 启动。
        //archivedCache就是归档缓存
        static Integer[] archivedCache;

        //静态代码块在类加载时执行,负责初始化缓存池。
        //IntegerCache 的 static 块会在第一次调用 Integer.valueOf(int) 或使用自动装箱时触发,也就说会触发缓存池
        static {
            // high value may be configured by property
            // 上界可以通过配置这个属性
            int h = 127;//此时配置的上界是127


            //你可以通过java -Djava.lang.Integer.IntegerCache.high=设置最高值
            //java -Djava.lang.Integer.IntegerCache.high=256里设置为256
            String integerCacheHighPropValue =
                VM.getSavedProperty("java.lang.Integer.IntegerCache.high");


            //integerCacheHighPropValue 就是你设置的值
            //如果integerCacheHighPropValue不为空
            if (integerCacheHighPropValue != null) {
                try {
                    //1.integerCacheHighPropValue转换成int值
                    //2.比较 默认的127的和你设置的值,取最大
                    h = Math.max(parseInt(integerCacheHighPropValue), 127);
                    // Maximum array size is Integer.MAX_VALUE
                    /

                    /* 
                    Integer.MAX_VALUE 是 Java 中 int 类型的最大值,等于 2^31 - 1,即 2147483647。它表示 int 类型能够存储的最大正整数。

                    low 是缓存池的下界,默认值为 -128。
                    (-low) 表示下界的绝对值,即 128。

                    Integer.MAX_VALUE - (-low)
                    这部分计算了从 low 到 Integer.MAX_VALUE 的整数个数。
                    公式:Integer.MAX_VALUE - (-low) 等价于 Integer.MAX_VALUE + low。

                    Integer.MAX_VALUE - (-low) - 1
                    减去 1 是为了确保数组的长度不会超出 Integer.MAX_VALUE。
                    数组的长度由 (high - low) + 1 决定,因此需要限制 high 的最大值。
                    */

                    //这个就是保证h在合法int范围内
                    h = Math.min(h, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                    //如果这个属性不能被解析为整数,忽略它
                }
            }
            //赋值给high
            high = h;

            // Load IntegerCache.archivedCache from archive, if possible
            //从归档文件中加载缓存,如果存在的话
            CDS.initializeFromArchive(IntegerCache.class);
                
            // 计算容量
            // high - low = 127 - (-128) = 255
            // + 1是因为要算上边界 
            // eg.6 - 1 = 5, 但是之间这是之间差5个数 
            // 如果 把左边界1算上,那就是 1 2 3 4 5
            // 那还少一个6,所以要加1
            int size = (high - low) + 1;

            // Use the archived cache if it exists and is large enough
            //从归档文件里查找如果没有该缓存或者是所需容量的size>大于缓存的长度
            //就创建一个新的数组
            if (archivedCache == null || size > archivedCache.length) {
                //创一个size大小的数组
                Integer[] c = new Integer[size];
                //j=-128
                int j = low;
                //-128一直自增到c.length 也就是256
                //但是是加到255就停止,因为i < c.length而不是等于 i < 256
                for(int i = 0; i < c.length; i++) {
                    c[i] = new Integer(j++);
                }
                //把新生成的数组给归档文件
                archivedCache = c;
            }
            //再给cache,到这里就完成了缓存
            cache = archivedCache;
            //确保IntegerCache.high的值至少为127,即保证了缓存至少包含从-128到127的所有整数值,确保合理范围
            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        //这是一个静态类,不需要实例化,所以需要私有
        private IntegerCache() {}
    }

归档文件

归档文件是一个特殊的文件,通常命名为 classes.jsa(Java Shared Archive),用于存储以下内容:

  • 类元数据:如类的结构信息。
  • 常量池:如字符串常量、方法引用等。
  • 缓存池 :如 IntegerCache 的缓存数组。

它的主要目的是:

  1. 加速 JVM 启动:通过预加载常用类的数据,减少类加载的时间。
  2. 节省内存:多个 JVM 实例可以共享同一个归档文件,避免重复加载相同的类数据。

归档文件的内容

归档文件中存储了以下类型的数据:

(1) 类元数据
  • 类的结构信息,包括类的字段、方法、继承关系等。
  • 这些数据在 JVM 加载类时会被直接使用,避免重新解析 .class 文件。
(2) 常量池
  • 常量池中存储了类中的常量数据,例如:
    • 字符串常量(如 "Hello")。
    • 方法引用、字段引用等符号引用。
  • 这些数据可以直接从归档文件中读取,而无需重新解析。
(3) 缓存池
  • 某些类(如 IntegerCache)的缓存数据也会被存储到归档文件中。
  • 例如,IntegerCache 的缓存数组(archivedCache)可以从归档文件中加载,避免每次运行时重新创建。
相关推荐
Golinie6 小时前
【Go | 从0实现简单分布式缓存】-1:LRU缓存淘汰策略与单机并发缓存
分布式·缓存·golang
清风微凉 aaa6 小时前
Redis-03高级篇中-多级缓存:
数据库·redis·缓存
一个假的前端男6 小时前
node 使用 Redis 缓存
数据库·redis·缓存
羊村懒哥6 小时前
高性能内存对象缓存Memcached详细实验操作
数据库·缓存·memcached
左灯右行的爱情7 小时前
Redis-缓存过期和内存淘汰
前端·redis·缓存
羊村懒哥8 小时前
网络缓存加速技术解析:从诞生到演进
缓存
祈澈菇凉8 小时前
如何清理cache-loader生成的缓存目录?
缓存
嘵奇8 小时前
MyBatis框架七:缓存
缓存·mybatis
明达技术8 小时前
网关断网缓存:让网络连接更可靠
缓存
库库林_沙琪马17 小时前
Redis 持久化:从零到掌握
数据库·redis·缓存