ボクシングとキャッシュ

Integerのキャッシュ

int がボクシングされると java.lang.Integer になりますが、このとき Integer.valueOf(int) が使われます。 このことは次のようなボクシングされるコードを書いてコンパイルしてからjavapすればよく分かります。

public class IntBoxingSample {
    public Integer boxing(int i) {
        return i;
    }
}

javapしてみた結果は次の通り。

Compiled from "IntBoxingSample.java"
public class IntBoxingSample {
  public IntBoxingSample();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public java.lang.Integer boxing(int);
    Code:
       0: iload_1
       1: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
       4: areturn
}

で、この Integer.valueOf(int) ですが、 -128 から 127 までの範囲はキャッシュされます。

このことは 言語仕様の5.1.7Integer.valueOf(int)のJavadoc に書かれています。

でもって、OpenJDKのコードを見た感じ、 java.lang.Integer.IntegerCache.high というシステムプロパティでキャッシュする範囲を変更できそうです。

というわけで次のようなコードを書いてコンパイルして普通に実行すると false と表示されますが、 -Djava.lang.Integer.IntegerCache.high=1000 という風にシステムプロパティを設定して実行すると true と表示されます。

public class Sample {
    public static void main(String[] args) {
        Integer a = 1000;
        Integer b = 1000;
        System.out.println(a == b);
    }
}

実行結果は次の通り。

% java Sample
false
% java -Djava.lang.Integer.IntegerCache.high=1000 Sample
true

他のプリミティブも見てみた

ByteShortLongInteger と同じく -128 から 127 までキャッシュされていました。 ただしキャッシュの範囲は変更できません。

それから Character0 から 127 までキャッシュされていました。 ASCII文字コードの NUL から DEL ですね。

Boolean は定数 TRUEFALSE のどちらかを返すようになっています。

最後に FloatDouble ですが、どちらもキャッシュせず常にインスタンス化するようになっていました。 浮動小数点数なので -128.0 から 127.0 の間にも膨大な量のインスタンスを生成し得るので、まあ、そりゃキャッシュしないか、という感じ。

まとめ

以上のように普段は意識しないような部分でキャッシュしておりパフォーマンス向上を図っていたりしています。 こういったJDKの努力に感謝しつつ、今後も意識せずにコーディングしようと思います。