Integer类的缓存机制
一、Integer类的缓存机制
我们查看Integer的源码,就会发现里面有个静态内部类。
public static Integer valueOf(int i) { assert IntegerCache.high >= 127; //当前值在缓存数组区间段,则直接返回该缓存值 if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; //否则创建新的Integer实例 return new Integer(i); } private static class IntegerCache { static final int low = -128; static final int high; static final Integer cache[]; //IntegerCache初始化时,缓存数值为-128-127的Integer实例(默认是从-128到127)。 static { // high value may be configured by property int h = 127; String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue != null) { int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } high = h; cache = new Integer[(high - low) + 1]; int j = low; //填充缓存数组 for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); } private IntegerCache() {} }
该类的作用是将数值等于-128-127(默认)区间的Integer实例缓存到cache数组中。通过valueOf()方法很明显发现,当再次创建值在-128-127区间的Integer实例时,会复用缓存中的实例,也就是直接指向缓存中的Integer实例。注意,这里的创建不包括用new创建,new创建对象不会复用缓存实例。
Integer的默认缓存范围为-128到127,可以通过jvm参数改变这个范围。
- 缓存上界high可以通过jvm参数-XX:AutoBoxCacheMax=size指定,取指定值与127的最大值并且不超过Integer表示范围,
- 下界不能指定,只能为-128。
实际上,可以将Integer的缓存机制理解为享元模式,享元模式可以参考这篇博文:享元模式。
二、其它具有缓存机制的类
实际上不仅仅Integer具有缓存机制,Byte、Short、Long、Character都具有缓存机制。来看看Long类中的缓存类
private static class LongCache { private LongCache(){} static final Long cache[] = new Long[-(-128) + 127 + 1]; static { for(int i = 0; i < cache.length; i++) cache[i] = new Long(i - 128); } }
ByteCache用于缓存Byte对象,ShortCache用于缓存Short对象,LongCache用于缓存Long对象,CharacterCache用于缓存Character对象。这些类都有缓存的范围,其中Byte,Short,Integer,Long为 -128 到 127,Character范围为 0 到 127。除了 Integer 可以通过jvm参数改变范围外,其它的都不行。
三、测试题
来做几道题测试一下把。
试题1
//情景1 Integer c = 128; Integer d = 128; System.out.println(c == d);//false //情景2 Integer a = 1; Integer b = 1; System.out.println(a == b);//true。 b.intValue() //情景3 Integer e = new Integer(1); Integer f = new Integer(1); System.out.println(e == f);//false
试题2
//情景4 int a = 1; Integer b = Integer.valueOf(1); Integer c = new Integer(1); System.out.println(a == b);//true System.out.println(a == c);//true
分析:a是基本类型,b和c是引用类型,两者进行比较时有一个拆箱的过程,也就是会默认调用b和c的intValue()方法。
//拆箱 public int intValue() { return value; }
最终比较的是基本类型的值,自然是相等的。
试题3
//代码来源于《深入理解Java虚拟机》第4章4.3.1 P121。 public class SynAddRunnable implements Runnable { int a, b; public SynAddRunnable(int a, int b) { this.a = a; this.b = b; } @Override public void run() { synchronized (Integer.valueOf(a)) { synchronized (Integer.valueOf(b)) { System.out.println(a + b); } } } public static void main(String[] args) { for (int i = 0; i < 100; i++) { new Thread(new SynAddRunnable(1, 2)).start(); new Thread(new SynAddRunnable(2, 1)).start(); } } }
上面这段程序会发生死锁。造成死锁的原因:[-128,127]之间的数字会被缓存,而Integer.valueOf()会返回缓存的对象。因此代码中200次for循环实际上总共只创建了两个对象,当线程A持有Integer.valueOf(1)时,如果线程B持有Integer.valueOf(2),则就会出现死锁,属于动态锁顺序死锁。
总结
1.Byte、Short、Integer、Long、Character都是具有缓存机制的类。缓存工作都是在静态块中完成,在类生命周期的初始化阶段执行。
2.缓存范围?
Byte,Short,Integer,Long为 -128 到 127
Character范围为 0 到 127
3. Integer可以通过jvm参数指定缓存范围,其它类都不行。
Integer的缓存上界high可以通过jvm参数-XX:AutoBoxCacheMax=size指定,取指定值与127的最大值并且不超过Integer表示范围,而下界low不能指定,只能为-128。