浅谈Java中Integer的缓存机制及源码解析

问题产生

前几天在写其他博客的时候,遇到一个问题。直接贴代码:

public class WrapperClassTest {
    public static void main(String[] args) {
        Integer a = 3;
        Integer b = 3;
        System.out.println(a == b);

        Integer c = 129;
        Integer d = 129;
        System.out.println(c == d);

    }
}

运行结果如下:
在这里插入图片描述
当时产生这样的疑问:为什么同样是自动装箱,两个结果却截然不同?
查阅相关资料及查看Integer类源码了解到原来Integer类内部提供了缓存机制。
这种 Integer 缓存策略仅在自动装箱(autoboxing)的时候有用,使用构造器创建的 Integer 对象不能被缓存。

问题探究

首先,我们通过反编译,看下JVM是如何完成自动装箱的。
在这里插入图片描述
通过反编译后的字节码可以看到,JVM在执行Integer i = 3;时,调用了Integerd的静态方法valueOf()。也就是说JVM通过使用Integer.valueOf()进行自动装箱。Integer a = 3;在执行代码时实际上是Integer a = Integer.valueOf(3);
那么下面我们来看下valueOf()方法的源码,看看JVM具体是如何进行自动装箱的。

valueOf()方法源码

    /**
     * 返回一个表示指定的int值的Integer实例。
	 * 如果不需要新的Integer实例,则通常应优先使用此方法,而不是构造方法Integer(int)。
	 * 因为此方法有可能通过缓存经常请求的值而显著提高空间和时间性能。
	 * 此方法将始终缓存-128至127(包括)范围内的值,并且可能缓存该范围之外的其他值。
     * @param  int值i
     * @return 代表i的Integer实例。
     * @since  1.5
     */
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

通过valueOf()方法的源码和注释可以看出,出于优化时间和空间性能方面的考虑,Integer提供了缓存——IntegerCache默认用于缓存-128至127的值。自动装箱时,若i的大小在IntegerCache范围内,则从缓存中取出对应Integer实例并返回;若i的大小超出IntegerCache的范围,则new一个值为i的Integer实例并返回。如上例Integer a = 3;Integer b = 3;a和b指向的是缓存中的同一个Integer实例,a == b;的结果必然是true。而Integer c = 129;Integer d = 129;超出了Integer的缓存范围,自动装箱时将分别返回两个值为129的Integer实例。c和d指向的是堆中不同的对象,c == d;的结果当然为false。
在valueOf()方法中,出现了IntegerCache,我们来看下IntegerCache又是什么。

IntegerCache源码

下面我们来看下源码,Integer类是如何做缓存的。

/**
     * 缓存以支持JLS要求的-128到127(含)之间的值的自动装箱的对象标识语义。
	 * 首次使用时会初始化缓存。缓存的大小可以由-XX:AutoBoxCacheMax = <size>选项控制。
	 * 在VM初始化期间,可以设置java.lang.Integer.IntegerCache.high属性并将其保存在
	 * sun.misc.VM类的私有系统属性中。
*/
private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static final Integer cache[];

    static {
        // high值可由属性配置
        int h = 127;
        String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            try {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // 数组的最大大小为Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) - 1);
            } catch (NumberFormatException nfe) {
                // 如果属性不能被解析成int,则忽略它.
            }
        }
        high = h;

        cache = new Integer[(high - low) + 1];
        int j = low;
        for (int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);

        // 值的范围必须留置在[-128, 127](JLS7 5.1.7)
        assert IntegerCache.high >= 127;
    }

    private IntegerCache() {
    }
}

首先IntegerCache是Integer类中的私有静态内部类,该类中有一个Integer数组cache,IntegerCache类第一次使用时,即被加载时通过静态代码块初始化cache。cache的缓存范围默认为从-128至127,可以通过-XX:AutoBoxCacheMax=来控制cache的size。缓存的最小值为-127不可修改,最大值可通过-XX:AutoBoxCacheMax=修改,即为的大小,但不超过Integer.MAX_VALUE - (-low) - 1

通过配置JVM参数修改Integer缓存的范围

在这里插入图片描述
在这里插入图片描述
通过以上配置及运行结果过可以看出-XX:AutoBoxCacheMax=<size>修改了Integer缓存的范围。

补充

一、包装类的缓存情况

除了Integer实现了缓存,其他包装类也缓存了缓存机制。以下为八种基本数据类型的包装类的缓存情况

包装类型缓存范围说明
Byte-128~127缓存范围不可修改
Short-128~127缓存范围不可修改
Integer-128~127默认缓存的最大值为127,可通过-XX:AutoBoxCacheMax=<size>修改缓存的最大值
Long-128~127缓存范围不可修改
Character0~127缓存的是字符编码表中十进制数0至127表示的字符
Booleantrue, false通过public static final变量TRUEFALSE缓存
Float——无缓存
Double——无缓存

二、为什么缓存范围是-128~127

通过上表可以看出包装类Byte、Short、Integer、Long的缓存大小均是-128~127。那为什么它们的缓存范围是-128~127呢?个人理解是出于节省内存的考虑。基本数据类型byte、short、int、long分别占1个字节、2个字节、4个字节、8个字节。字节是计算机存储信息的基本单位,计算机存储信息的基本单位,所以将一个字节表示的最大二进制数范围的数用于缓存。而1个字节有8位,最高位是符号位,最高位为0表示正数,最高位为1表示负数。所以1个字节所能表示的最大正数为01111111,即2^7-1,转为十进制为127;所能表示的最小负数为11111111,即-2^7,转为十进制为-128。因此以上包装类的缓存范围为-128~127

三、包装类缓存的位置

又上可知,包装类的缓存是在编译期首次使用XXXCache时初始化的,缓存值实际上是存储在运行时常量池中。因此,jdk1.7之前包装类的缓存值存储在方法区中的运行时常量池,jdk1.8之后运行时常量池移至元空间。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luffylv

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值