Integer類的快取策略
先給出一個問題,下面程式的輸出結果是什麼
public static void integerCacheTest{
Integer num_01 = 100;
Integer num_02 = 100;
System.out.println(num_01==num_02);
Integer num_03 = 200;
Integer num_04 = 200;
System.out.println(num_03==num_04);
}
我們知道String的策略是,如果直接賦值,先檢測常量池中有沒有這個物件,如果沒有,就在堆記憶體中new一個物件,然後放到常量池中,之後再有同樣的值就直接在常量池中取出來就可以,不用new.
那麼Integer裡面是不是也是這樣呢?如果是,兩次輸出應該都為true.如果不是,兩次結果應該都為false.
我們來看看執行結果
怎麼回事?怎麼是一個true,一個false.難道是第一個用了類似String的策略,第二個沒用?如果用了這樣的策略,那他應該有個範圍,這個範圍又是什麼呢?我們來用一個程式驗證一下
測試程式
public static void integerCacheTest(){
/*Integer num_01 = 100;
Integer num_02 = 100;
System.out.println(num_01==num_02);
Integer num_03 = 200;
Integer num_04 = 200;
System.out.println(num_03==num_04);*/
for(int i = 0;i<200;i++){
Integer num_05 = i;
Integer num_06 = i;
boolean flag = num_05==num_06;
System.out.println(i+":"+flag);
}
}
輸出結果
0:true
1:true
2:true
3:true
4:true
.....
125:true
126:true
127:true
128:false
129:false
130 :false
...
197:false
198:false
199:false
可見,在0-127範圍內,用Integer直接賦值的變數,是不新建物件的,大於128之後的整數,每次都要新建物件.
這是為什麼呢? 看了一下Integer的原始碼,原來Integer中有一個內部類IntegerCache
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
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) {
try {
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);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
這個類又跟Integer num = 100;
這樣的語句有什麼關係呢.
這裡就要用到java中自動拆箱裝箱的知識點了,參見我的博文https://blog.csdn.net/fly_fly_fly_pig/article/details/82459166
Integer num = 100;
這樣的語句實際是呼叫的Integer.valueOf()方法來進行自動裝箱.而valueOf的原始碼中可以看出
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
在建立Integer物件之前,會判斷這個數在不在IntegerCache.cache內,如果在,直接返回對應的陣列元素,如果不在,new一個物件返回. 這個內部類中不難看出,cache這個陣列的範圍是-128~127,所以在此範圍之內的,都不會新建物件.
Javadoc 詳細的說明這個類是用來實現快取支援,並支援 -128 到 127 之間的自動裝箱過程。最大值 127 可以通過 JVM 的啟動引數 -XX:AutoBoxCacheMax=size 修改。 快取通過一個 for 迴圈實現。從小到大的建立儘可能多的整數並存儲在一個名為 cache 的整數陣列中。這個快取會在 Integer 類第一次被使用的時候被初始化出來。以後,就可以使用快取中包含的例項物件,而不是建立一個新的例項(在自動裝箱的情況下)。
實際上在 Java 5 中引入這個特性的時候,範圍是固定的 -128 至 +127。後來在 Java 6 中,最大值對映到 java.lang.Integer.IntegerCache.high,可以使用 JVM 的啟動引數設定最大值。這使我們可以根據應用程式的實際情況靈活地調整來提高效能。是什麼原因選擇這個 -128 到 127 這個範圍呢?因為這個範圍的整數值是使用最廣泛的。 在程式中第一次使用 Integer 的時候也需要一定的額外時間來初始化這個快取。
Java 語言規範中的快取行為
在 Boxing Conversion 部分的Java語言規範(JLS)規定如下: 如果一個變數 p 的值屬於:-128至127之間的整數(§3.10.1這個估計是版本號吧),true 和 false的布林值 (§3.10.3),’u0000′ 至 ‘u007f’ 之間的字元(§3.10.4)中時,將 p 包裝成 a 和 b 兩個物件時,可以直接使用 a == b 判斷 a 和 b 的值是否相等。
其他快取的物件 這種快取行為不僅適用於Integer物件。我們針對所有整數型別的類都有類似的快取機制。 有 ByteCache 用於快取 Byte 物件 有 ShortCache 用於快取 Short 物件 有 LongCache 用於快取 Long 物件 有 CharacterCache 用於快取 Character 物件 Byte,Short,Long 有固定範圍: -128 到 127。對於 Character, 範圍是 0 到 127。除了 Integer 可以通過引數改變範圍外,其它的都不行。