では ①キャッシュの仕組みの確認(-128〜127) と ②オートボクシングのパフォーマンス比較(プリミティブ vs ラッパー) を実験するための Java の実行可能なコード を用意します。手元のJVMでコンパイルして実行すれば、結果を観察できます。各プログラムに解説と注意点も付けました。
1. 整数キャッシュの仕組みを確かめるコード
ファイル名:IntegerCacheDemo.java
public class IntegerCacheDemo {
public static void main(String[] args) {
// キャッシュ範囲内のテスト
Integer a1 = Integer.valueOf(127);
Integer b1 = Integer.valueOf(127);
System.out.println("127: a1 == b1 -> " + (a1 == b1)); // 期待: true
// キャッシュ範囲外のテスト
Integer a2 = Integer.valueOf(128);
Integer b2 = Integer.valueOf(128);
System.out.println("128: a2 == b2 -> " + (a2 == b2)); // 期待: false(通常)
// new を使うと常に別オブジェクト
Integer n1 = new Integer(127);
Integer n2 = new Integer(127);
System.out.println("new 127: n1 == n2 -> " + (n1 == n2)); // 期待: false
// equals は値比較
System.out.println("equals check (128): " + a2.equals(b2)); // true
// 確認のために範囲を表示
System.out.println("キャッシュ範囲の一般的な説明: JVM は -128〜127 をキャッシュ(デフォルト)します。");
}
}
Java実行方法
javac IntegerCacheDemo.java
java IntegerCacheDemo
期待される出力(例)
127: a1 == b1 -> true
128: a2 == b2 -> false
new 127: n1 == n2 -> false
equals check (128): true
キャッシュ範囲の一般的な説明: JVM は -128〜127 をキャッシュ(デフォルト)します。
解説
Integer.valueOf()は -128〜127 の範囲で同じインスタンスを返す(JVMの実装で一般的)。そのため==(参照比較)がtrueになります。- 128 のような値はキャッシュ範囲外なので、別インスタンスが返され
==はfalseになります。 new Integer(...)は常に新しいオブジェクトを作るので==はfalse。値比較は必ずequals()を使うのが安全。
2. オートボクシングのパフォーマンス実験
ファイル名:BoxingBenchmark.java
public class BoxingBenchmark {
static final int N = 30_000_000; // 3千万ループ(環境に応じて調整)
static final int WARMUP = 5;
public static void main(String[] args) {
System.out.println("JVM: オートボクシング vs プリミティブの簡易ベンチマーク");
System.out.println("ループ回数 N = " + N);
System.out.println();
// JIT のウォームアップ(簡易)
for (int i = 0; i < WARMUP; i++) {
runPrimitive(1_000_000);
runBoxing(1_000_000);
}
// 本計測:プリミティブ
long t1 = runPrimitive(N);
// 本計測:ボクシング(Integer を使う)
long t2 = runBoxing(N);
System.out.printf("プリミティブ合計時間: %.3f ms%n", t1 / 1_000_000.0);
System.out.printf("ラッパー(Integer)合計時間: %.3f ms%n", t2 / 1_000_000.0);
System.out.printf("比率 (wrapper / primitive): %.2f%n", (double) t2 / t1);
}
// プリミティブ int を使うループ
static long runPrimitive(int loops) {
long start = System.nanoTime();
int sum = 0;
for (int i = 0; i < loops; i++) {
sum += i; // 全てプリミティブ操作
}
// sum を使って最適化を防ぐ
if (sum == -1) System.out.println("never");
long end = System.nanoTime();
return end - start;
}
// Integer(ラッパー)を使うループ(オートボクシング/アンボクシングが発生)
static long runBoxing(int loops) {
long start = System.nanoTime();
Integer sum = 0; // ボクシングされる(Integer.valueOf(0))
for (int i = 0; i < loops; i++) {
sum += i; // sum = Integer.valueOf(sum.intValue() + i) 相当(ボクシングとアンボクシング)
}
if (sum == -1) System.out.println("never");
long end = System.nanoTime();
return end - start;
}
}
Java実行方法
javac BoxingBenchmark.java
java -Xmx1G BoxingBenchmark
(必要なら -Xmx を大きめに。ループ回数 N は実行時間が長すぎる場合に調整してください。)
期待される結果(傾向)
プリミティブ合計時間はかなり短い。ラッパー(Integer)合計時間はプリミティブより 数倍〜十数倍 遅くなることが多い。比率 (wrapper / primitive)は環境によって異なるが、1より大きい(つまりラッパーの方が遅い) のが普通。
例(環境により大幅に異なるため参考値)
プリミティブ合計時間: 150 ms
ラッパー合計時間: 1200 ms
比率 (wrapper / primitive): 8.00
解説(なぜ差が出るか)
- ラッパーを使うと、オートボクシング/アンボクシング によりオブジェクト操作が発生する(
Integer.valueOf呼び出し、場合によっては新しいオブジェクト生成)。これが CPU とメモリの負荷になる。 - ガーベジコレクションの負荷やキャッシュ効率の低下も影響する。
- JIT(ホットスポット)による最適化の影響も大きく、短いベンチでは結果がばらつく。だから簡易ベンチでもウォームアップを行っている。
3. 実験時の注意とベンチ改善のコツ
- JVMのウォームアップ を忘れない(JIT 最適化の影響が大きい)。上のコードで簡易ウォームアップを入れています。
- System.nanoTime() を使う(
currentTimeMillisより精度が高い)。 - 最小化されたベンチは誤解を生みやすい:実際のアプリケーションのパターン(メモリ割り当て、GC、スレッド等)により差は変わる。
- ループ回数は適切に:短すぎるとノイズが大きい、長すぎると時間がかかる。
- 高精度のベンチをしたければ、JMH(Java Microbenchmark Harness) の利用を検討する(本格的なマイクロベンチ用ツール)。
4. 実務上の結論・推奨
- パフォーマンスが重要なループでは プリミティブ(
intやlong)を使う。 - コレクション(
ArrayList等)を使わざるを得ない場合は、可能ならプリミティブ配列(int[])を検討したり、性能問題があるならIntStreamやサードパーティのプリミティブ専用コレクション(例:fastutil 等)を検討する。 - キャッシュ範囲(-128〜127)に依存したコード(
==で比較するなど)はバグのもと。値比較はequals()またはアンボクシングして==を使う。 nullを扱う場合は慎重に(アンボクシングで NPE になる)。

