1. 程式人生 > >Integer類的快取策略

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 可以通過引數改變範圍外,其它的都不行。