1. 程式人生 > 其它 >Java之Integer$IntegerCache初探(AutoBoxCacheMax)

Java之Integer$IntegerCache初探(AutoBoxCacheMax)

JDK 8

---

int是Java的基本資料型別,而Integer是其包裝器類。

在建立Integer時,如果使用建構函式,則會在 堆中新建物件,而使用 valueOf的話,則 可能 會從其 內部類 IntegerCache 的 靜態常量 cache 中獲取資料。

“可能”是指 JDK預設情況下,cache中儲存的資料是 -128~127,共計 256個Integer物件。

Integer部分原始碼:

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
    
    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");
        
        }
        high = h;
        
        // ...
    }

IntegerCache會初始化一個Integer陣列——cache,最小值 -128,最大值 預設為 127,但可以從屬性 java.lang.Integer.IntegerCache.high 中獲取——從而修改最大值。

修改 java.lang.Integer.IntegerCache.high 的方式,在執行程式的時候新增下面的VM引數:

-XX:AutoBoxCacheMax=1024

注:1024是我自己設定的值。修改後,cache陣列的範圍就擴大了,但是,不能小於127——程式碼控制了的

測試程式碼:

	/**
	 * 測試 == 和 equals
	 * @author ben
	 * @date 2021-08-05 11:35:08 CST
	 */
	public static void testEquals() {
		cs.accept("testEquals...start");
		Integer i1 = 1;
		Integer i2 = new Integer(1);
		Integer i3 = new Integer("1");
		Integer i4 = Integer.valueOf(1);
		Integer i5 = Integer.valueOf("1");
		
		cs.accept("i1=" + i1);
		cs.accept("i2=" + i2);
		cs.accept("i3=" + i3);
		cs.accept("i4=" + i4);
		cs.accept("i5=" + i5);

		cs.accept("hashCode");
		cs.accept("i1 hashCode=" + i1.hashCode());
		cs.accept("i2 hashCode=" + i2.hashCode());
		cs.accept("i3 hashCode=" + i3.hashCode());
		cs.accept("i4 hashCode=" + i4.hashCode());
		cs.accept("i5 hashCode=" + i5.hashCode());

		cs.accept("identityHashCode");
		cs.accept("i1 identityHashCode=" + System.identityHashCode(i1));
		cs.accept("i2 identityHashCode=" + System.identityHashCode(i2));
		cs.accept("i3 identityHashCode=" + System.identityHashCode(i3));
		cs.accept("i4 identityHashCode=" + System.identityHashCode(i4));
		cs.accept("i5 identityHashCode=" + System.identityHashCode(i5));

		cs.accept("==:");
		cs.accept(i1 == i2);
		cs.accept(i1 == i3);
		cs.accept(i1 == i4);
		cs.accept(i1 == i5);
		cs.accept("equals:");
		cs.accept(i1.equals(i2));
		cs.accept(i1.equals(i3));
		cs.accept(i1.equals(i4));
		cs.accept(i1.equals(i5));
		
		cs.accept("");
		
		i1 = 999;
		i2 = new Integer(999);
		i3 = new Integer("999");
		i4 = Integer.valueOf(999);
		i5 = Integer.valueOf("999");
		
		cs.accept("i1=" + i1);
		cs.accept("i2=" + i2);
		cs.accept("i3=" + i3);
		cs.accept("i4=" + i4);
		cs.accept("i5=" + i5);

		cs.accept("hashCode:");
		cs.accept("i1 hashCode=" + i1.hashCode());
		cs.accept("i2 hashCode=" + i2.hashCode());
		cs.accept("i3 hashCode=" + i3.hashCode());
		cs.accept("i4 hashCode=" + i4.hashCode());
		cs.accept("i5 hashCode=" + i5.hashCode());

		cs.accept("identityHashCode:");
		cs.accept("i1 identityHashCode=" + System.identityHashCode(i1));
		cs.accept("i2 identityHashCode=" + System.identityHashCode(i2));
		cs.accept("i3 identityHashCode=" + System.identityHashCode(i3));
		cs.accept("i4 identityHashCode=" + System.identityHashCode(i4));
		cs.accept("i5 identityHashCode=" + System.identityHashCode(i5));

		cs.accept("==:");
		cs.accept(i1 == i2);
		cs.accept(i1 == i3);
		cs.accept(i1 == i4);
		cs.accept(i1 == i5);
		cs.accept("equals:");
		cs.accept(i1.equals(i2));
		cs.accept(i1.equals(i3));
		cs.accept(i1.equals(i4));
		cs.accept(i1.equals(i5));
		cs.accept("testEquals...end.");
		
		String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
		System.out.println("integerCacheHighPropValue=" + integerCacheHighPropValue);
	}

注:== 除了比較值是否相同,還會比較物件的地址——對於類物件來說;equals 只是比較值。

1、未修改時測試結果

測試-預設
testEquals...start
i1=1
i2=1
i3=1
i4=1
i5=1
hashCode
i1 hashCode=1
i2 hashCode=1
i3 hashCode=1
i4 hashCode=1
i5 hashCode=1
identityHashCode
i1 identityHashCode=135721597
i2 identityHashCode=142257191
i3 identityHashCode=1044036744
i4 identityHashCode=135721597
i5 identityHashCode=135721597
==:
false
false
true
true
equals:
true
true
true
true

i1=999
i2=999
i3=999
i4=999
i5=999
hashCode:
i1 hashCode=999
i2 hashCode=999
i3 hashCode=999
i4 hashCode=999
i5 hashCode=999
identityHashCode:
i1 identityHashCode=1826771953
i2 identityHashCode=1406718218
i3 identityHashCode=245257410
i4 identityHashCode=1705736037
i5 identityHashCode=455659002
==:
false
false
false
false
equals:
true
true
true
true
testEquals...end.
integerCacheHighPropValue=null

2、修改為1024時測試結果

測試1024
testEquals...start
i1=1
i2=1
i3=1
i4=1
i5=1
hashCode
i1 hashCode=1
i2 hashCode=1
i3 hashCode=1
i4 hashCode=1
i5 hashCode=1
identityHashCode
i1 identityHashCode=135721597
i2 identityHashCode=142257191
i3 identityHashCode=1044036744
i4 identityHashCode=135721597
i5 identityHashCode=135721597
==:
false
false
true
true
equals:
true
true
true
true

i1=999
i2=999
i3=999
i4=999
i5=999
hashCode:
i1 hashCode=999
i2 hashCode=999
i3 hashCode=999
i4 hashCode=999
i5 hashCode=999
identityHashCode:
i1 identityHashCode=1826771953
i2 identityHashCode=1406718218
i3 identityHashCode=245257410
i4 identityHashCode=1826771953
i5 identityHashCode=1826771953
==:
false
false
true
true
equals:
true
true
true
true
testEquals...end.
integerCacheHighPropValue=1024

3、修改為0時測試結果

測試0
testEquals...start
i1=1
i2=1
i3=1
i4=1
i5=1
hashCode
i1 hashCode=1
i2 hashCode=1
i3 hashCode=1
i4 hashCode=1
i5 hashCode=1
identityHashCode
i1 identityHashCode=135721597
i2 identityHashCode=142257191
i3 identityHashCode=1044036744
i4 identityHashCode=135721597
i5 identityHashCode=135721597
==:
false
false
true
true
equals:
true
true
true
true

i1=999
i2=999
i3=999
i4=999
i5=999
hashCode:
i1 hashCode=999
i2 hashCode=999
i3 hashCode=999
i4 hashCode=999
i5 hashCode=999
identityHashCode:
i1 identityHashCode=1826771953
i2 identityHashCode=1406718218
i3 identityHashCode=245257410
i4 identityHashCode=1705736037
i5 identityHashCode=455659002
==:
false
false
false
false
equals:
true
true
true
true
testEquals...end.
integerCacheHighPropValue=0

結果分析:

1、3的結果相同,因為不能設定為小於 127的值;

2設定為1024後,999也會存在於快取了;

所有使用 new Integer() 的 都是 對記憶體物件,和cache中的 使用 == 時,返回false。

另外檢查了 Character、Byte、Short、Long,都有cache機制,但是,都是固定的 -128~127,不能通過配置更改。

    // 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);
        }
    }

上面測試的JDK版本:Java HotSpot

>java -version
java version "1.8.0_202"
Java(TM) SE Runtime Environment (build 1.8.0_202-b08)
Java HotSpot(TM) 64-Bit Server VM (build 25.202-b08, mixed mode)

我使用的IDE是Eclipse,版本 Version: 2021-03 (4.19.0),其預設提供了JRE來執行Java程式,支援 到了 Java 15:

在使用這個jre來執行程式會怎樣呢?

出錯了:sun.misc.VM.getSavedProperty 沒法使用

並且Integer的兩個 建構函式被標記為 @Deprecated(since="9"),用valueOf就好了。

    @Deprecated(since="9")
    public Integer(int value) {
        this.value = value;
    }

IntegerCache獲取 cache的最大值的方式:

    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer[] cache;
        static Integer[] archivedCache;

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                VM.getSavedProperty("java.lang.Integer.IntegerCache.high");

可是,在測試程式裡面執行 VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); 時 出現了異常:

Exception in thread "main" java.lang.IllegalAccessError: class aug.Test80201 (in unnamed module @0x3caeaf62) 
cannot access class jdk.internal.misc.VM (in module java.base) because module java.base does not export 
jdk.internal.misc to unnamed module @0x3caeaf62
	at aug.Test80201.testEquals(Test80201.java:167)
	at aug.Test80201.main(Test80201.java:35)

不過,測試前面的配置——-XX:AutoBoxCacheMax=1024,仍然是有效的。

這個jre的版本是多少呢?

Runtime.version()
15.0.2+7-27
System.getProperty("java.version")
15.0.2

最開始測試使用的JDK版本(程式輸出):

1.8.0_202

兩者存在不同,使用 Eclipse新版本需要注意咯

而且,某些1.8版的函式,在 jre 這個裡面 是沒有的。

參考文件

1、Java之IntegerCache簡單介紹

使用反射來修改 cache的值很有意思!

2、Java Integer的快取策略

3、