10

将为a和分配多少字节b

import android.graphics.Bitmap;

Bitmap[][][] a = new Bitmap[1000][2][2];
Bitmap[][][] b = new Bitmap[2][2][1000];

请注意,我只询问纯数组占用的内存,内部没有对象。

我为什么要问?因为我正在写一个安卓游戏。对我来说顺序无所谓,但如果有内存差异,还是省一些就好了。

4

4 回答 4

10

是的,它确实有所作为。

在 Java 中,2D 数组是 1D 数组的数组,并且数组(像所有对象一样)除了保存元素本身所需的空间外,还具有标题。

所以考虑int[10][2]vs int[2][10],并假设一个 32 位 JVM。

  • int[2][10]由一个 2 个元素的数组和 2 个 10 个元素的数组组成。总计 - 3 个数组对象 + 22 个元素。
  • int[10][2]由一个 10 个元素的数组和 2 个元素的 10 个数组组成。总计 - 11 个数组对象 + 30 个元素。

如果我们假设标头大小是 3 个 32 位字(典型的 32 位 JVM)并且引用是 1 个 32 位字,那么

  • int[2][10]占用 3*3 + 22*1 = 31 个字 = 124 个字节
  • int[10][2]占用 11*3 + 30*1 = 63 个字 = 252 个字节

应用相同的逻辑,您可以估计具有更多维数的数组的大小。

但很明显,如果最大维度是最右边的维度,则使用的空间更少。


我已经用int数组完成了数学运算,但是在 32 位机器上 anint和 areference占用相同的字节数。在 64 位机器上,引用的大小可以与 aint或 a相同long,具体取决于 JVM 选项。标头大小也可能不同......不完全确定......可能取决于平台。

我没有考虑容纳Bitmap对象本身所需的空间,但是无论您如何组织数组,它都是一样的。

于 2013-03-11T13:09:38.890 回答
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 中情况并非如此,其中多维数组实际上是一组嵌套数组。这意味着二维数组的每一行都有一个对象的开销,因为它实际上是一个单独的对象!

于 2013-03-11T13:13:48.247 回答
3

在热点上尝试时(确切的数字可能与您在 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);
    }
}
于 2013-03-11T13:09:33.310 回答
-1

没有内存差异,但您的数组索引的顺序 - 理论上 - 可能会影响程序的速度。

您通常在嵌套循环中处理多维数组内容。因此,您的数组应该以您在内部循环中处理相邻元素的方式组织,以允许编译器生成最有效的代码。我不知道 Java 如何组织内存,但认为它与 C/C++ 没有什么不同:

int a[10][100];
for (i = 0; i < 10; ++i) {
    for (j = 0; j < 100; ++j) {
        do_something_with(a[i][j]);
    }
}
于 2013-03-11T13:08:00.950 回答