深入剖析Integer的緩存機制

,我們對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的緩存機制


這又是為什麼呢? 按照我們上一篇的分析,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? 按理,我們也是需要擴充負數的緩存範圍的。至今也沒有一個開發人員給出合理的解析。


分享到:


相關文章: