Может ли порядок в объявлении многомерного массива влиять на используемую память?
Сколько байтов будет выделено для a
и b
?
import android.graphics.Bitmap;
Bitmap[][][] a = new Bitmap[1000][2][2];
Bitmap[][][] b = new Bitmap[2][2][1000];
Обратите внимание, что я только спрашиваю о памяти, сделанной чистыми массивами, внутри нет объектов.
Почему я спрашиваю? Потому что я пишу Android-игру. Для меня порядок не имеет значения, но если есть разница в памяти, будет полезно сохранить некоторые.
Ответы
Ответ 1
Да, это действительно имеет значение.
В Java 2D-массив представляет собой массив из 1D массивов, а массивы (как и все объекты) имеют заголовки в дополнение к пространству, которое необходимо для хранения самих элементов.
Итак, рассмотрим int[10][2]
по сравнению с int[2][10]
и предположим 32-разрядную JVM.
-
int[2][10]
состоит из одного массива из 2 элементов и 2 массивов из 10 элементов. Всего - 3 объекта массива + 22 элемента.
-
int[10][2]
состоит из одного массива из 10 элементов и 10 массивов из 2 элементов. Всего - 11 объектов массива + 30 элементов.
Если предположить, что размер заголовка составляет 3 32-разрядных слова (типичный для 32-битной JVM), а ссылка - это 32-битное слово, то
-
int[2][10]
принимает 3 * 3 + 22 * 1 = 31 слов = 124 байта
-
int[10][2]
принимает 11 * 3 + 30 * 1 = 63 слова = 252 байта
Примените ту же логику, и вы можете оценить размер массивов с большим количеством измерений.
Но ясно, что вы используете меньшее пространство, если наибольшее измерение является самым правым.
Я выполнил математику с массивами int
, но на 32-битной машине int
и reference
занимают одинаковое количество байтов. На 64-битной машине ссылка может быть того же размера, что и int
или long
, в зависимости от параметров JVM. Размеры заголовков также могут быть разными... не совсем уверенными... потенциально зависимыми от платформы.
Я не учитывал пространство, необходимое для хранения объектов Bitmap
, но это одно и то же, но вы организуете массивы.
Ответ 2
При попытке использовать точку доступа (точные цифры могут отличаться от тех, которые вы получаете на dalvik, но выводы должны быть похожими), я получаю следующие результаты:
Массив объектов (1000x2x2): 76034 байт
Объектный массив (2x2x1000): 16137 байт
Это соответствует приблизительному вычислению:
[2][2][1000]
Array # Header Size Memory Number Total
1 16 2 24 1 24
2 16 2 24 2 48
3 16 1000 4016 4 16,064
Grand Total 16,136
[1000][2][2]
Array # Header Size Memory Number Total
1 16 1000 4016 1 4,016
2 16 2 24 1000 24,000
3 16 2 24 2000 48,000
Grand Total 76,016
Проверочный код ниже, запустите с -XX:-UseTLAB
, чтобы получить более точные результаты.
public class TestMemory {
private static final int SIZE = 100;
private static Runnable r;
private static Object o;
private static void test(Runnable r, String name, int numberOfObjects) {
long mem = Runtime.getRuntime().freeMemory();
r.run();
System.out.println(name + ": " + (mem - Runtime.getRuntime().freeMemory()) / numberOfObjects + " bytes");
}
public static void main(String[] args) throws Exception {
r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new Object[1000][2][2];} };
test(r, "Object array (1000x2x2)", SIZE);
r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new Object[2][2][1000];} };
test(r, "Object array (2x2x1000)", SIZE);
}
}
Ответ 3
Да, это имеет значение. Просто попробуйте это с помощью -Xmx8M
:
// throws OutOfMemoryError
public static void main(String[] args) {
int[][] a = new int[500000][2];
System.out.println("a.length: '" + (a.length) + "'");
}
// works
public static void main(String[] args) {
int[][] a = new int[2][500000];
System.out.println("a.length: '" + (a.length) + "'");
}
Первый будет вызывать OutOfMemoryError, второй пройдет.
Причина в том, что первая версия создает 500 000 массивов длиной 2, а вторая создает 2 массива длиной 500 000.
Ссылка:
В языке, таком как C, двумерный массив (или даже любой многомерный массив) по существу является одномерным массивом с манипуляцией разумным указателем. Это не так в Java, где многомерный массив на самом деле представляет собой набор вложенных массивов. Это означает, что каждая строка двумерного массива имеет накладные расходы на объект, поскольку на самом деле это отдельный объект!
Ответ 4
Нет разницы в памяти, но порядок индексов массива может
- в теории - влияют на скорость вашей программы.
Обычно вы обрабатываете многомерные массивы в вложенных циклах. Таким образом, ваш массив должен быть организован таким образом, чтобы вы адресули соседние элементы во внутреннем цикле, чтобы компилятор мог создать наиболее эффективный код. Я не знаю, как Java организует память, но думаю, что она не отличается от C/С++:
int a[10][100];
for (i = 0; i < 10; ++i) {
for (j = 0; j < 100; ++j) {
do_something_with(a[i][j]);
}
}