Может ли порядок в объявлении многомерного массива влиять на используемую память?

Сколько байтов будет выделено для 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]);
    }
}