Автоупаковка по сравнению с ручной упаковкой в Java

Почему вторая часть кода быстрее?

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));
    }
}
21
задан MC Emperor 24 January 2017 в 09:41
поделиться

3 ответа

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

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

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

48
ответ дан 29 November 2019 в 06:25
поделиться

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

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

14
ответ дан 29 November 2019 в 06:25
поделиться

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

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

7
ответ дан 29 November 2019 в 06:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: