- PVSM.RU - https://www.pvsm.ru -
Размеры объектов в Java уже обсуждались на Хабре, например, здесь [1] или здесь [2]. Мне бы хотелось подробнее остановиться на размерах многомерных массивов — простая вещь, которая для меня стала неожиданной.
Оптимизируя вычислительный алгоритм по памяти, я наткнулся на то, что при определённых (вполне разумных) входных параметрах создаётся массив float[500][14761][2]. Сколько он может занимать в памяти (на HotSpot 1.6.0_26 32bit, если кому интересно)? Я примерно прикинул, что 500*14 761*2*sizeof(float) = 500*14 761*2*4 = 59 044 000 байт плюс какой-то оверхед. Решив проверить, как на самом деле, я воспользовался Eclipse Memory Analyzer [3] (невероятно волшебная вещь, рекомендую!) и обнаружил, что «Retained Heap» для этого массива составляет 206 662 016 байт! Неплохой оверхед — 350%. Посмотрим, почему так получилось.
Я поискал, что на эту тему пишут в Интернете, и наткнулся на довольно понятную статью на английском «How to calculate the memory usage of a Java array [4]». Вкратце суть изложения такая:
Этого знания достаточно. Итак мы имеем в конце цепочки массивы float[2]. Их размер — это 2 float (по 4 байта) + 12 байт оверхеда — 20 байт. Выравниваем до 8 — получается 24 байта. Всего у нас таких массивов создано в куче 500*14 761 — 7 380 500 штук. Итого они весят 177 132 000 байт.
Затем у нас есть массивы float[14761][] — массивы из 14 761 ссылок на другие массивы. Каждый такой массив занимает 14 761 ссылки (по 4 байта) + 12 байт оверхеда — 59 056 байт (делится на 8 — выравнивать не надо). Всего этих массивов 500 штук, значит они вместе весят 29 528 000 байт.
Наконец, у нас есть собственно тот массив, который мы завели — float[500][][] — массив из 500 ссылок на двумерные массивы. Он занимает 500*4+12 = 2 012, да ещё 4 байта выравнивания — 2 016 байт.
Складываем, что получилось: 177 132 000 + 29 528 000 + 2 016 = 206 662 016 — как раз то число, которое показал Memory Analyzer.
Сразу же пришло в голову очевидное решение: упорядочить измерения массива по возрастанию. Сделаем float[2][500][14761] и алгоритм от этого никак не пострадает. Какой размер получится в этом случае?
В сумме 59 060 056 байт, оверхед меньше 0.03%, почти 150 мегабайт памяти спасено.
Отсюда следует правило большого пальца: если размеры массива по измерениям хотя бы примерно известны заранее, следует упорядочивать измерения по возрастанию. Кроме того, обязательно используйте анализаторы памяти: узнаете много нового о своей программе.
Автор: lany
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/java/5792
Ссылки в тексте:
[1] здесь: http://habrahabr.ru/post/134102/
[2] здесь: http://habrahabr.ru/post/124909/
[3] Eclipse Memory Analyzer: http://www.eclipse.org/mat/
[4] How to calculate the memory usage of a Java array: http://www.javamex.com/tutorials/memory/array_memory_usage.shtml
Нажмите здесь для печати.