Почему вторая часть кода быстрее?
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));
}
}
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
, и, по крайней мере, тогда при оптимизации скалярной замены этот особый случай не принимался во внимание. Я не знаю, улучшилась ли с тех пор оптимизация.
Автобокс будет использовать Integer.valueOf
и Double.valueOf
. При вызове этих методов возникают некоторые накладные расходы (хотя в конечном итоге они будут встроены). Также Integer.valueOf
выполняет некоторую проверку на низкие значения для использования объединенных экземпляров, что не всегда является преимуществом в вашем коде (хотя это может немного уменьшить размер кучи). Объединенные экземпляры могут быть преимуществом, если они уменьшают размер кучи, время сборки мусора и могут даже улучшать производительность проверки равенства.
Но в целом это микрооптимизация, которую вы должны игнорировать.
Поскольку результаты микротестов ненадежны?
Кроме того, автоматическая упаковка выполняется с помощью Integer.valueOf () и Double.valueOf (), а не конструкторов.