Вопрос

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

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));
    }
}
Это было полезно?

Решение

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

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

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

Другие советы

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top