【轉】理解Java Integer的緩存策略

本文將介紹 Java 中 Integer 緩存的相關知識。這是 Java 5 中引入的一個有助於節省內存、提升性能的特性。首先看一個使用 Integer 的示例代碼,展現了 Integer 的緩存行爲。接着咱們將學習這種實現的緣由和目的。你能夠先猜猜下面 Java 程序的輸出結果。很明顯,這裏有一些小陷阱,這也是咱們寫這篇文章的緣由。html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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" );
 
     }
}

大多數人都認爲上面的兩個判斷的結果都是 false。雖然它們的值相等,但因爲比較的是對象,而對象的引用不同,因此會認爲兩個 if 判斷都是 false 的。在 Java 中,== 比較的是對象引用,而 equals 比較的是值。所以,在這個例子中,不一樣的對象有不一樣的引用,因此在進行比較的時候都應該返回 false。可是奇怪的是,這裏兩個類似的 if 條件判斷卻返回不一樣的布爾值。java

下面是上面代碼真正的輸出結果,數組

1
2
integer1 == integer2
integer3 != integer4

Java 中 Integer 緩存實現緩存

在 Java 5 中,爲 Integer 的操做引入了一個新的特性,用來節省內存和提升性能。整型對象在內部實現中經過使用相同的對象引用實現了緩存和重用。ide

上面的規則適用於整數區間 -128 到 +127。性能

這種 Integer 緩存策略僅在自動裝箱(autoboxing)的時候有用,使用構造器建立的 Integer 對象不能被緩存。學習

Java 編譯器把原始類型自動轉換爲封裝類的過程稱爲自動裝箱(autoboxing),這至關於調用 valueOf 方法ui

1
2
Integer a = 10 ; //this is autoboxing
Integer b = Integer.valueOf( 10 ); //under the hood

如今咱們知道了 JDK 源碼中對應實現的部分在哪裏了。咱們來看看 valueOf 的源碼。下面是 JDK 1.8.0 build 25 中的代碼。this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
     * Returns an {<a href="http://www.jobbole.com/members/java12">@code</a> Integer} instance representing the specified
     * {<a href="http://www.jobbole.com/members/java12">@code</a> int} value.  If a new {<a href="http://www.jobbole.com/members/java12">@code</a> Integer} instance is not
     * required, this method should generally be used in preference to
     * the constructor {<a href="http://www.jobbole.com/members/57845349">@link</a> #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 {<a href="http://www.jobbole.com/members/java12">@code</a> int} value.
     * @return an {<a href="http://www.jobbole.com/members/java12">@code</a> Integer} instance representing {<a href="http://www.jobbole.com/members/java12">@code</a> i}.
     * <a href="http://www.jobbole.com/members/chchxinxinjun">@since</a>  1.5
     */
    public static Integer valueOf( int i) {
        if (i &amp;gt;= IntegerCache.low &amp;amp;&amp;amp; i &amp;lt;= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

在建立新的 Integer 對象以前會先在 IntegerCache.cache 中查找。有一個專門的 Java 類來負責 Integer 的緩存。spa

IntegerCache 類

IntegerCache 是 Integer 類中一個私有的靜態類。咱們來看看這個類,有比較詳細的文檔,能夠提供咱們不少信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
      * 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 {<a href="http://www.jobbole.com/members/java12">@code</a> -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 &amp;lt; cache.length; k++)
                 cache[k] = new Integer(j++);
 
             // range [-128, 127] must be interned (JLS7 5.1.7)
             assert IntegerCache.high &amp;gt;= 127 ;
         }
 
         private IntegerCache() {}
     }

Javadoc 詳細的說明這個類是用來實現緩存支持,並支持 -128 到 127 之間的自動裝箱過程。最大值 127 能夠經過 JVM 的啓動參數 -XX:AutoBoxCacheMax=size 修改。 緩存經過一個 for 循環實現。從小到大的建立儘量多的整數並存儲在一個名爲 cache 的整數數組中。這個緩存會在 Integer 類第一次被使用的時候被初始化出來。之後,就可使用緩存中包含的實例對象,而不是建立一個新的實例(在自動裝箱的狀況下)。

實際上在 Java 5 中引入這個特性的時候,範圍是固定的 -128 至 +127。後來在 Java 6 中,最大值映射到 java.lang.Integer.IntegerCache.high,可使用 JVM 的啓動參數設置最大值。這使咱們能夠根據應用程序的實際狀況靈活地調整來提升性能。是什麼緣由選擇這個 -128 到 127 這個範圍呢?由於這個範圍的整數值是使用最普遍的。 在程序中第一次使用 Integer 的時候也須要必定的額外時間來初始化這個緩存。

Java 語言規範中的緩存行爲

在 Boxing Conversion 部分的Java語言規範(JLS)規定以下:

若是一個變量 p 的值屬於:-128至127之間的整數(§3.10.1),true 和 false的布爾值 (§3.10.3),’u0000′ 至 ‘u007f’ 之間的字符(§3.10.4)中時,將 p 包裝成 a 和 b 兩個對象時,能夠直接使用 a == b 判斷 a 和 b 的值是否相等。

其餘緩存的對象

這種緩存行爲不只適用於Integer對象。咱們針對全部整數類型的類都有相似的緩存機制。

有 ByteCache 用於緩存 Byte 對象

有 ShortCache 用於緩存 Short 對象

有 LongCache 用於緩存 Long 對象

有 CharacterCache 用於緩存 Character 對象

Byte,Short,Long 有固定範圍: -128 到 127。對於 Character, 範圍是 0 到 127。除了 Integer 能夠經過參數改變範圍外,其它的都不行。

原文連接: javapapers 翻譯: ImportNew.com 挖坑的張師傅
譯文連接: http://www.importnew.com/18884.html
轉載請保留原文出處、譯者和譯文連接。]

相關文章
相關標籤/搜索