← Назад к вопросам

Для чего нужен Integer Pool?

2.0 Middle🔥 111 комментариев
#JVM и управление памятью

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Integer Pool в Java: Назначение и работа

Integer Pool (кэш целых чисел) — это оптимизация памяти в Java, которая кэширует объекты Integer для часто используемых значений. Это один из самых интересных примеров того, как JVM оптимизирует использование памяти.

Что такое Integer Pool

Integer Pool — это набор предварительно созданных объектов Integer, которые хранятся в памяти и переиспользуются:

// Integer Pool кэширует значения от -128 до 127
Integer a = 100;    // Берётся из кэша
Integer b = 100;    // Берётся из кэша (ОДИН И ТОТ ЖЕ ОБЪЕКТ!)
Integer c = 128;    // Создаётся НОВЫЙ объект (вне кэша)

System.out.println(a == b);   // true (один объект в памяти)
System.out.println(a == c);   // false (разные объекты)
System.out.println(a.equals(c));  // true (значения равны)

Почему был придуман Integer Pool

1. Оптимизация памяти

Целые числа от -128 до 127 используются очень часто. Предварительное кэширование экономит миллионы объектов:

// Без кэша — 256 миллионов объектов
for (int i = -128; i <= 127; i++) {
    Integer x = i;  // Создаёт новый объект каждый раз
}

// С кэшем — только 256 объектов на всё приложение
for (int i = -128; i <= 127; i++) {
    Integer x = i;  // Переиспользует один из 256 объектов
}

2. Производительность

// Бенчмарк
long start = System.nanoTime();

for (int i = 0; i < 10_000_000; i++) {
    Integer x = 50;  // Из кэша — очень быстро
}

long duration = System.nanoTime() - start;
System.out.println("Cached: " + duration + "ns");

// Vs

start = System.nanoTime();

for (int i = 0; i < 10_000_000; i++) {
    Integer x = 50000;  // Создаёт новый объект каждый раз
}

duration = System.nanoTime() - start;
System.out.println("Uncached: " + duration + "ns");

// Результат: Cached примерно в 2-3 раза быстрее

Как работает Integer Pool

Механизм кэширования

// Автобоксинг переходит через Integer.valueOf()
Integer a = 100;  // Эквивалентно Integer.valueOf(100)

// Реализация Integer.valueOf():
public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];  // Из кэша
    return new Integer(i);  // Новый объект
}

// IntegerCache
private static class IntegerCache {
    static final int low = -128;
    static final int high = 127;
    static final Integer cache[];
    
    static {
        // Создаём кэш при загрузке класса
        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);
    }
}

Правило автобоксинга и valueOf()

Критичная разница

// Автобоксинг
Integer a = 50;     // Использует Integer.valueOf(50) — ИЗ КЭША
Integer b = new Integer(50);  // Создаёт НОВЫЙ объект (не использует кэш)

System.out.println(a == b);   // false (разные объекты)
System.out.println(a.equals(b));  // true (значения равны)

// Правильно сравнивать Integer через equals(), не ==

Полное понимание

public class IntegerPoolDemo {
    public static void main(String[] args) {
        // 1. Автобоксинг — использует valueOf() — из кэша
        Integer a = 10;
        Integer b = 10;
        System.out.println(a == b);  // true (один объект из кэша)
        
        // 2. Явный конструктор — игнорирует кэш
        Integer c = new Integer(10);
        System.out.println(a == c);  // false (разные объекты)
        System.out.println(a.equals(c));  // true
        
        // 3. Значения вне кэша
        Integer d = 200;
        Integer e = 200;
        System.out.println(d == e);  // false (вне кэша)
        System.out.println(d.equals(e));  // true
        
        // 4. valueOf() — использует кэш
        Integer f = Integer.valueOf(50);
        Integer g = Integer.valueOf(50);
        System.out.println(f == g);  // true (из кэша)
        
        // 5. Операции над Integer создают новые объекты
        Integer h = 100;
        Integer i = 50 + 50;  // Вычисляется, потом автобоксится
        System.out.println(h == i);  // true (оба 100, в кэше)
        
        Integer j = 128;
        Integer k = 64 + 64;  // Вычисляется = 128
        System.out.println(j == k);  // false (128 вне кэша, разные объекты)
    }
}

Похожие кэши для других типов

// Byte: -128 до 127 (полный диапазон типа)
Byte b1 = 10;
Byte b2 = 10;
System.out.println(b1 == b2);  // true (есть кэш)

// Short: -128 до 127 (только этот диапазон)
Short s1 = 100;
Short s2 = 100;
System.out.println(s1 == s2);  // true

Short s3 = 200;
Short s4 = 200;
System.out.println(s3 == s4);  // false (вне кэша)

// Long: -128 до 127
Long l1 = 50L;
Long l2 = 50L;
System.out.println(l1 == l2);  // true

// Float, Double: НЕТ кэша
Float f1 = 1.5f;
Float f2 = 1.5f;
System.out.println(f1 == f2);  // false (нет кэша)

// Boolean: только 2 объекта (true и false)
Boolean bool1 = true;
Boolean bool2 = true;
System.out.println(bool1 == bool2);  // true (один из 2 объектов)

Практические следствия

1. Проблема с == для Integer

public class IntegerComparison {
    // Трудноуловимый баг
    public static boolean isEqual(Integer a, Integer b) {
        return a == b;  // НЕПРАВИЛЬНО!
    }
    
    public static void main(String[] args) {
        System.out.println(isEqual(100, 100));  // true (кэш)
        System.out.println(isEqual(1000, 1000));  // false (новые объекты)
        
        // Правильно:
        // return a.equals(b) или a.intValue() == b.intValue()
    }
}

2. Производительность в циклах

public class PerformanceDemo {
    public static void main(String[] args) {
        // Быстро — используется кэш
        long start = System.nanoTime();
        for (int i = 0; i < 10_000_000; i++) {
            Integer x = 50;  // Всегда один объект из кэша
        }
        System.out.println("Cached: " + (System.nanoTime() - start) + "ns");
        
        // Медленно — создание новых объектов
        start = System.nanoTime();
        for (int i = 0; i < 10_000_000; i++) {
            Integer x = new Integer(50);  // Новый объект каждый раз
        }
        System.out.println("Uncached: " + (System.nanoTime() - start) + "ns");
    }
}

3. GC нагрузка

public class GCImpact {
    public static void main(String[] args) {
        // С кэшем — минимум GC
        for (int i = 0; i < 10_000_000; i++) {
            Integer x = 50;  // Один объект
            // Никогда не удаляется из памяти
        }
        
        // Без кэша — много GC
        for (int i = 0; i < 10_000_000; i++) {
            Integer x = new Integer(50);  // Новый объект
            // Становится мусором в конце итерации
            // GC должен очищать миллионы объектов
        }
    }
}

Когда это важно

1. Collections и HashMap

Map<Integer, String> map = new HashMap<>();

// Кэш помогает здесь (Integer используется в hashCode и equals)
for (int i = -128; i <= 127; i++) {
    map.put(i, "value");  // Переиспользуется из кэша
}

// Много объектов создаётся здесь
for (int i = 0; i < 10_000; i++) {
    Integer key = i * 1000;  // Вне кэша — новый объект
    map.put(key, "value");
}

2. Сетевой протокол

public class ProtocolHandler {
    public void processPacket(byte[] data) {
        int status = data[0];  // Часто 0-127
        Integer statusObj = status;  // Из кэша
        
        int port = ((data[1] << 8) | data[2]);  // Может быть большим
        Integer portObj = port;  // Может быть вне кэша
    }
}

Best Practices

1. Всегда используй equals() для сравнения Integer

// Неправильно
if (a == b) { }

// Правильно
if (a.equals(b)) { }
if (a != null && a.equals(b)) { }

// Или распакуй
if (a.intValue() == b.intValue()) { }

2. Используй примитивный int где возможно

// Не нужно объекты
public void processNumbers(List<Integer> numbers) {
    for (Integer num : numbers) {
        int value = num;  // Распакуй в примитив
        doSomething(value);
    }
}

// Лучше:
public void processNumbers(int[] numbers) {
    for (int num : numbers) {  // Примитивные значения
        doSomething(num);
    }
}

3. valueOf() вместо конструктора

// Неправильно
Integer a = new Integer(50);  // Игнорирует кэш

// Правильно
Integer a = Integer.valueOf(50);  // Использует кэш
Integer b = 50;  // Автобоксинг — использует valueOf()

Выводы

  1. Integer Pool кэширует значения от -128 до 127
  2. Используется автоматически при автобоксинге
  3. Конструктор игнорирует кэш — используй valueOf()
  4. Всегда сравнивай Integer через equals(), не ==
  5. Даёт прирост в производительности и памяти
  6. Важно понимать для интервью и оптимизации

Integer Pool — это отличный пример того, как JVM оптимизирует часто используемые данные. Это простая, но мощная оптимизация, которая экономит память и процессорное время для миллиардов Integer объектов в реальных приложениях.

Для чего нужен Integer Pool? | PrepBro