英文原文:Java Integer Cache 翻譯地址:Java中整型的緩存機(jī)制 原文作者:Java Papers 翻譯作者:Hollis 轉(zhuǎn)載請(qǐng)注明出處询筏。
本文將介紹Java中Integer的緩存相關(guān)知識(shí)声搁。這是在Java 5中引入的一個(gè)有助于節(jié)省內(nèi)存凤粗、提高性能的功能仿滔。首先看一個(gè)使用Integer的示例代碼嘉裤,從中學(xué)習(xí)其緩存行為廊驼。接著我們將為什么這么實(shí)現(xiàn)以及他到底是如何實(shí)現(xiàn)的据过。你能猜出下面的Java程序的輸出結(jié)果嗎。如果你的結(jié)果和真正結(jié)果不一樣妒挎,那么你就要好好看看本文了绳锅。
package com.javapapers.java;
public class JavaIntegerCache {
public static void main(String... strings) {
Integer integer1 = 3;
Integer integer2 = 3;
if (integer1 == integer2)
System.out.println("integer1 == integer2");
else
System.out.println("integer1 != integer2");
Integer integer3 = 300;
Integer integer4 = 300;
if (integer3 == integer4)
System.out.println("integer3 == integer4");
else
System.out.println("integer3 != integer4");
}
}
我們普遍認(rèn)為上面的兩個(gè)判斷的結(jié)果都是false。雖然比較的值是相等的酝掩,但是由于比較的是對(duì)象鳞芙,而對(duì)象的引用不一樣,所以會(huì)認(rèn)為兩個(gè)if判斷都是false的期虾。在Java中积蜻,==
比較的是對(duì)象應(yīng)用,而equals
比較的是值彻消。所以竿拆,在這個(gè)例子中,不同的對(duì)象有不同的引用宾尚,所以在進(jìn)行比較的時(shí)候都將返回false丙笋。奇怪的是谢澈,這里兩個(gè)類(lèi)似的if條件判斷返回不同的布爾值。
上面這段代碼真正的輸出結(jié)果:
integer1 == integer2
integer3 != integer4
Java中Integer的緩存實(shí)現(xiàn)
在Java 5中御板,在Integer的操作上引入了一個(gè)新功能來(lái)節(jié)省內(nèi)存和提高性能锥忿。整型對(duì)象通過(guò)使用相同的對(duì)象引用實(shí)現(xiàn)了緩存和重用。
適用于整數(shù)值區(qū)間-128 至 +127怠肋。
只適用于自動(dòng)裝箱敬鬓。使用構(gòu)造函數(shù)創(chuàng)建對(duì)象不適用。
Java的編譯器把基本數(shù)據(jù)類(lèi)型自動(dòng)轉(zhuǎn)換成封裝類(lèi)對(duì)象的過(guò)程叫做自動(dòng)裝箱
笙各,相當(dāng)于使用valueOf
方法:
Integer a = 10; //this is autoboxing
Integer b = Integer.valueOf(10); //under the hood
現(xiàn)在我們知道了這種機(jī)制在源碼中哪里使用了钉答,那么接下來(lái)我們就看看JDK中的valueOf
方法。下面是JDK 1.8.0 build 25
的實(shí)現(xiàn):
/**
* Returns an {@code Integer} instance representing the specified
* {@code int} value. If a new {@code Integer} instance is not
* required, this method should generally be used in preference to
* the constructor {@link #Integer(int)}, as this method is likely
* to yield significantly better space and time performance by
* caching frequently requested values.
*
* This method will always cache values in the range -128 to 127,
* inclusive, and may cache other values outside of this range.
*
* @param i an {@code int} value.
* @return an {@code Integer} instance representing {@code i}.
* @since 1.5
*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
在創(chuàng)建對(duì)象之前先從IntegerCache.cache中尋找杈抢。如果沒(méi)找到才使用new新建對(duì)象数尿。
IntegerCache Class
IntegerCache是Integer類(lèi)中定義的一個(gè)private static
的內(nèi)部類(lèi)。接下來(lái)看看他的定義惶楼。
/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/
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() {}
}
其中的javadoc詳細(xì)的說(shuō)明了緩存支持-128到127之間的自動(dòng)裝箱過(guò)程右蹦。最大值127可以通過(guò)-XX:AutoBoxCacheMax=size
修改。 緩存通過(guò)一個(gè)for循環(huán)實(shí)現(xiàn)歼捐。從低到高并創(chuàng)建盡可能多的整數(shù)并存儲(chǔ)在一個(gè)整數(shù)數(shù)組中何陆。這個(gè)緩存會(huì)在Integer類(lèi)第一次被使用的時(shí)候被初始化出來(lái)。以后豹储,就可以使用緩存中包含的實(shí)例對(duì)象甲献,而不是創(chuàng)建一個(gè)新的實(shí)例(在自動(dòng)裝箱的情況下)。
實(shí)際上這個(gè)功能在Java 5中引入的時(shí)候,范圍是固定的-128 至 +127颂翼。后來(lái)在Java 6中晃洒,可以通過(guò)java.lang.Integer.IntegerCache.high
設(shè)置最大值。這使我們可以根據(jù)應(yīng)用程序的實(shí)際情況靈活地調(diào)整來(lái)提高性能朦乏。到底是什么原因選擇這個(gè)-128到127范圍呢球及?因?yàn)檫@個(gè)范圍的數(shù)字是最被廣泛使用的。 在程序中呻疹,第一次使用Integer的時(shí)候也需要一定的額外時(shí)間來(lái)初始化這個(gè)緩存吃引。
Java語(yǔ)言規(guī)范中的緩存行為
在Boxing Conversion部分的Java語(yǔ)言規(guī)范(JLS)規(guī)定如下:
如果一個(gè)變量p的值是:
-128至127之間的整數(shù)(§3.10.1)
true 和 false的布爾值 (§3.10.3)
‘\u0000’至 ‘\u007f’之間的字符(§3.10.4)
中時(shí),將p包裝成a和b兩個(gè)對(duì)象時(shí)刽锤,可以直接使用a==b判斷a和b的值是否相等镊尺。
其他緩存的對(duì)象
這種緩存行為不僅適用于Integer對(duì)象。我們針對(duì)所有的整數(shù)類(lèi)型的類(lèi)都有類(lèi)似的緩存機(jī)制并思。
有ByteCache用于緩存Byte對(duì)象
有ShortCache用于緩存Short對(duì)象
有LongCache用于緩存Long對(duì)象
有CharacterCache用于緩存Character對(duì)象
Byte
, Short
, Long
有固定范圍: -128 到 127庐氮。對(duì)于Character
, 范圍是 0 到 127。除了Integer
以外宋彼,這個(gè)范圍都不能改變弄砍。