Автобокс против ручного бокса в Java - PullRequest
20 голосов
/ 22 февраля 2010

Почему второй фрагмент кода быстрее?

Map<Integer, Double> map = new HashMap<Integer, Double>();
for (int i = 0; i < 50000; i++) {
    for (double j = 0.0; j < 10000; j++) {
        map.put(i, j);
    }
}

Map<Integer, Double> map=new HashMap<Integer, Double>();
for (int i = 0; i < 50000; i++) {
    for (double j = 0.0; j < 10000; j++) {            
        map.put(new Integer(i), new Double(j));
    }
}

Ответы [ 3 ]

47 голосов
/ 22 февраля 2010

Автобокс использует Integer.valueOf, который внутренне кэширует целочисленные объекты для маленьких целых чисел (по умолчанию от -128 до 127, но максимальное значение можно настроить с помощью свойства "java.lang.Integer.IntegerCache.high" - см. Источник код Integer.valueOf), поэтому он отличается от вызова new Integer напрямую. Поскольку Integer.valueOf быстро проверяет величину целочисленного значения перед вызовом new Integer, немного быстрее вызывать new Integer напрямую (хотя он использует больше памяти, если у вас много маленьких целых чисел). Распределение в Java очень быстрое, и время выполнения GC пропорционально количеству живых короткоживущих объектов (т.е. не пропорционально количеству мусора), поэтому GC также очень быстр.

Но в зависимости от версии JVM и от того, какие оптимизации включены, существует скалярная оптимизация замещения, которая может привести к гораздо большей разнице в производительности при распределении недолговечных объектов (в вашем примере, что оптимизация не может быть выполнена, потому что Вы храните объекты на карте, но во многих других ситуациях это полезно).

В последних версиях JVM существует скалярная замена оптимизация (за исключением 1.6.0_18, где аварийный анализ временно отключен ), что означает, что распределение недолговечных объектов может быть оптимизировано далеко. Когда скалярная замена в JVM была новой, кто-то сделал эталоном , где был код, похожий на ваш. В результате код, использующий примитивы, был самым быстрым, код с явными вызовами new Integer() был почти таким же быстрым, как и код, использующий примитивы, а код, использующий автобокс, был намного медленнее. Это произошло потому, что в автобоксировании используется Integer.valueOf, и, по крайней мере, тогда оптимизация скалярной замены не учитывала этот особый случай. Я не знаю, была ли оптимизация улучшена с тех пор.

14 голосов
/ 22 февраля 2010

При автобоксировании будут использоваться Integer.valueOf и Double.valueOf. При вызове этих методов возникают некоторые издержки (хотя в конечном итоге они будут встроены). Также Integer.valueOf выполняет некоторую проверку на наличие низких значений для использования объединенных экземпляров, что не часто является победой в вашем коде (хотя это может немного уменьшить размер кучи). Объединенные экземпляры могут быть выигрышем, когда они уменьшают размер кучи, время GC и могут даже улучшить производительность теста на равенство.

Но, в общем, это микрооптимизация, которую вы должны игнорировать.

7 голосов
/ 22 февраля 2010

Поскольку результаты микробенчмарков ненадежны?

Кроме того, автобокс выполняется с использованием Integer.valueOf () и Double.valueOf (), а не конструкторов.

...