在Java編程中,Integer類作為基本類型int的包裝器,提供了對象化的操作和自動裝箱與拆箱的功能。從JDK5開始引入了一項特別的優化措施——Integer緩存機制,它對於提升程式性能和減少記憶體消耗具有重要意義。接下來我們由一段代碼去打開Integer緩存機制的秘密。 public static ...
在Java編程中,Integer
類作為基本類型int的包裝器,提供了對象化的操作和自動裝箱與拆箱的功能。從JDK5
開始引入了一項特別的優化措施——Integer緩存機制,它對於提升程式性能和減少記憶體消耗具有重要意義。接下來我們由一段代碼去打開Integer緩存機制的秘密。
public static void main(String[] args) {
Integer i1 = 100;
Integer i2 = 100;
System.out.println(i1 == i2);
Integer i3 = 1000;
Integer i4 = 1000;
System.out.println(i3 == i4);
}
至於答案是什麼呢?我們接著往下看,等你看完就明白了。
當你在你的Idea中寫出這段代碼的時候,Idea就會提示你要使用
equals()
方法區比較大小,因為Integer
是對象,對象的值比較要用equals()
方法,而不是使用==
,這裡我們主要是研究一下Integer
的緩存機制。
Integer緩存是什麼
Java的Integer
類內部實現了一個靜態緩存池,用於存儲特定範圍內的整數值對應的Integer
對象。預設情況下,這個範圍是-128至127。當通過Integer.valueOf(int)
方法創建一個在這個範圍內的整數對象時,並不會每次都生成新的對象實例,而是復用緩存中的現有對象。我們看一下Integer.valueOf(int)
的源碼:
@HotSpotIntrinsicCandidate
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
對於Integer.valueOf(int)
方法來說,由於這個方法經常用於將基本類型int轉換為包裝器對象,所以它使用了@HotSpotIntrinsicCandidate
註解,這樣HotSpot JVM可能會提供一種更為高效的內部實現來處理自動裝箱操作。而IntegerCache
是Integer
內部的一個靜態類,負責緩存整數對象。它在類載入時被初始化,創建並緩存範圍內的所有整數對象。我們看一下IntegerCache
的源碼:
private static class IntegerCache {
// 緩存範圍的下限,預設為-128
static final int low = -128;
// 緩存範圍的上限,初始化時動態計算(基於系統屬性或預設值127)
static final int high;
// 存儲在緩存範圍內所有Integer對象的數組
static final Integer cache[];
// 靜態初始化塊,在類載入時執行
static {
// 初始設定high為127
int h = 127;
// 嘗試從系統屬性獲取用戶自定義的最大整數值
String integerCacheHighPropValue =
VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
// 如果系統屬性存在並且可以轉換為int類型,則更新high值
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
// 確保high至少為127,並且不超過Integer.MAX_VALUE允許的最大數組大小
h = Math.max(i, 127);
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
}
}
// 設置最終確定的high值
high = h;
// 初始化cache數組,長度等於緩存範圍內的整數數量
cache = new Integer[(high - low) + 1];
// 使用迴圈填充cache數組,創建並存儲對應的Integer對象
int j = low;
for(int k = 0; k < cache.length; k++) {
cache[k] = new Integer(j++);
}
// 檢查,確保緩存範圍至少包含[-128, 127]
// 這是Java語言規範對小整數自動裝箱共用的要求
assert IntegerCache.high >= 127;
}
// 私有構造器,防止外部實例化此內部類的對象
private IntegerCache() {}
}
IntegerCache
類在Java虛擬機啟動時創建了一個固定大小的數組,用於緩存指定範圍內所有的Integer
對象。這樣在後續程式運行過程中,對於這些範圍內的整數進行裝箱操作時,可以直接從緩存中獲取已存在的對象,以提升性能並減少記憶體開銷。同時,它也提供了根據系統屬性(-Djava.lang.Integer.IntegerCache.high
)來自定義緩存上限的能力,並確保滿足Java語言規範關於小整數自動裝箱共用的規定。
在Integer.value(int)
方法中,如果int
的值在IntegerCache
返回的low
和high
之內,則直接返回IntegerCache
中緩存的對象,否則重新new
一個新的Integer
對象。
而文章開頭示例中,我們使用Interge i1 = 100
的方式其實是Java的自動裝箱機制,整數字面量100
是一個基本類型的int值。當賦值給一個Integer
引用變數i
時,編譯器會隱式地調用Integer.valueOf(int)
方法將這個基本類型的int值轉換為Integer
對象。
整數在編程中經常被使用,特別是在迴圈計數等場景中,通過緩存整數對象,可以大幅度減少相同整數值的對象創建,從而減小記憶體占用。
由此我們可以看出因為100在[-128, 127]之內,所以i1 == i2
列印true
,而1000不在[-128, 127]之內,所以i3 == i4
列印false
。
我們嘗試使用java.lang.Integer.IntegerCache.high
調整一下high
為1000,然後看一下效果:
列印結果都是true。
當然這個上限不要隨意去調整,調整之前,需要仔細評估應用程式的實際需求和性能影響。儘量選擇在[-128, 127]範圍內的整數值,以充分利用Integer緩存機制。
註意事項
-
比較: 由於緩存的存在,在-128至127之間的
Integer
對象在進行==
運算符比較時,結果可能是true
,因為它們指向的是同一個記憶體地址。而在緩存範圍之外創建的Integer
對象即使值相等,也會視為不同的對象,因此使用==
比較會返回false
。不論是否啟用緩存,對於任何兩個Integer
對象,只要其包含的整數值相同,調用equals()
方法始終會返回true
。所以我們在比較對象時一定要使用equals()
方法。 -
不適用於所有場景: 當使用
new Integer(i)
直接創建Integer
對象時,不會利用緩存。 -
不要隨意去擴展緩存的上下限
總結
Integer緩存機制是Java中的一項性能優化措施,通過緩存一定範圍內的整數對象,既能減小記憶體開銷,又能提高性能。
本文已收錄於我的個人博客:碼農Academy的博客,專註分享Java技術乾貨,包括Java基礎、Spring Boot、Spring Cloud、Mysql、Redis、Elasticsearch、中間件、架構設計、面試題、程式員攻略等