,我們對Long、Short、Byte三個類的構造方法、內部Cache類以及valueOf進行了分析。 看了文章的人,肯定心中有個疑惑,怎麼沒有對最為常用的Integer類進行分析? 實際上,正是由於Integer最為常用,JDK源碼對其內部緩存實現類IntegerCache和valueOf方法做了進一步的優化。
測試代碼
我們首先還是來看一段測試代碼。
<code>@Test
public void testInteger() {
int primaryInt = 127;
Integer int1 = Integer.valueOf(primaryInt);
Integer int2 = 127;
Assert.assertTrue(int1 == int2);
int1 = new Integer(127);
Assert.assertFalse(int1 == int2);
primaryInt = 128;
int1 = Integer.valueOf(primaryInt);
int2 = 128;
Assert.assertFalse(int1 == int2); // 1
int1 = new Integer(128);
Assert.assertFalse(int1 == int2);
}
複製代碼/<code>
上述測試代碼在不設置JVM參數的情況下,是可以通過測試的。 但是一旦我們在運行該測試的時候設置了JVM參數-Djava.lang.Integer.IntegerCache.high=255,這個測試將在 注析1處失敗!下圖為IntelliJ IDEA中JVM參數的設置。
這又是為什麼呢? 按照我們上一篇的分析,Integer類提供的緩存機制應該也是緩存[-128,127]之間的值。 在不提供-Djava.lang.Integer.IntegerCache.high=255JVM參數的時候確實是對的。但是一旦提供了該參數,情況就發生了變化。 變化究竟是怎樣的?我們且看Integer的源代碼。
分析
我們打開java.lang.Integer的源代碼。其中關鍵性代碼如下。
<code>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 =
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() {}
}
複製代碼/<code>
上面是Integer的內部緩存類IntegerCache的源代碼,我們發現跟Long#LongCache的代碼要複雜不少。
- IntegerCache內部還是一樣使用的cache數組作為緩存數據的機制;
- IntegerCache使用了low、high分別作為緩存的數值的最小值和最大值;
- low實際上是一個固定的值-128;
- high是可以設置的: 通過設置變量h=127作為high的默認值; 通過VM.getSavedProperty("java.lang.Integer.IntegerCache.high")獲取java.lang.Integer.IntegerCache.high參數值視為變量integerCacheHighPropValue,並將 該值轉換為int,然後取127和該轉換後的int值中較大的作為變量i的值; 取i, Integer.MAX_VALUE - (-low) -1中較小的值作為h的值;這裡之所以取中較小的值,是因為數組size最大為Integer.MAX_VALUE。 也就是說數組元素下標最大為Integer.MAX_VALUE - 1的。而IntegerCache的cache數組中需要存放128個負數,所以cache的下標需要再減128也就是-low, 也就是Integer.MAX_VALUE - (-low) -1。 h再賦值給high作為能夠緩存的最大整數。
- 計算出high之後的代碼就是初始化cache數組的過程。上一篇已經分析過了,這裡就不再重複了。
從上述計算high的過程中可以看出,
java.lang.Integer.IntegerCache.high參數的設置必須要大於127的整數,否則該設置就是無效的。由於緩存機制的變化,Integer.valueOf的實現也發生一些變化,如下所示。 實際上,也就是隻對IntegerCache.low和IntegerCache.high之間的值返回緩存。
<code>public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
複製代碼/<code>
總結
- 如果項目需要大量重複使用範圍較大的Integer類型值的時候,請通過設置java.lang.Integer.IntegerCache.high參數來擴充緩存:
- 通過命令行運行的話,可以這樣設置:java -Djava.lang.Integer.IntegerCache.high=xxx Main.class
- 通過特定開發工具運行的話,直接設置運行參數:-Djava.lang.Integer.IntegerCache.high=xxx
- java.lang.Integer.IntegerCache.high參數的值應該是一個大於127的值,否則跟沒有設置效果是一樣的。
最後,這裡為什麼low是一個固定值-128? 按理,我們也是需要擴充負數的緩存範圍的。至今也沒有一個開發人員給出合理的解析。
閱讀更多 碼農大劉 的文章