歡迎光臨
每天分享高質量文章

Java 中整型的快取機制

(點選上方公眾號,可快速關註)


來源:張洪亮,

www.hollischuang.com/archives/1174

本文將介紹Java中Integer的快取相關知識。這是在Java 5中引入的一個有助於節省記憶體、提高效能的功能。首先看一個使用Integer的示例程式碼,從中學習其快取行為。接著我們將為什麼這麼實現以及他到底是如何實現的。你能猜出下麵的Java程式的輸出結果嗎。如果你的結果和真正結果不一樣,那麼你就要好好看看本文了。

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條件判斷傳回不同的布林值。

上面這段程式碼真正的輸出結果:

integer1 == integer2

integer3 != integer4

Java中Integer的快取實現

在Java 5中,在Integer的操作上引入了一個新功能來節省記憶體和提高效能。整型物件透過使用相同的物件取用實現了快取和重用。

適用於整數值區間-128 至 +127。

只適用於自動裝箱。使用建構式建立物件不適用。

Java的編譯器把基本資料型別自動轉換成封裝類物件的過程叫做自動裝箱,相當於使用valueOf方法:

Integer a = 10; //this is autoboxing

Integer b = Integer.valueOf(10); //under the hood

現在我們知道了這種機制在原始碼中哪裡使用了,那麼接下來我們就看看JDK中的valueOf方法。下麵是JDK 1.8.0 build 25的實現:

/**

     * 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);

    }

在建立物件之前先從IntegerCache.cache中尋找。如果沒找到才使用new新建物件。

IntegerCache Class

IntegerCache是Integer類中定義的一個private static的內部類。接下來看看他的定義。

/**

   * 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詳細的說明瞭快取支援-128到127之間的自動裝箱過程。最大值127可以透過-XX:AutoBoxCacheMax=size修改。 快取透過一個for迴圈實現。從低到高並建立盡可能多的整數並儲存在一個整數陣列中。這個快取會在Integer類第一次被使用的時候被初始化出來。以後,就可以使用快取中包含的實體物件,而不是建立一個新的實體(在自動裝箱的情況下)。

實際上這個功能在Java 5中引入的時候,範圍是固定的-128 至 +127。後來在Java 6中,可以透過java.lang.Integer.IntegerCache.high設定最大值。這使我們可以根據應用程式的實際情況靈活地調整來提高效能。到底是什麼原因選擇這個-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以外,這個範圍都不能改變。

看完本文有收穫?請轉發分享給更多人

關註「ImportNew」,提升Java技能

贊(0)

分享創造快樂