多维数组声明中的顺序是否会影响已用内存?

时间:2013-03-11 13:02:59

标签: java arrays memory

将为ab分配多少字节?

import android.graphics.Bitmap;

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

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

我为什么要问?因为我正在写一款Android游戏。对我来说,顺序无关紧要,但如果存在差异,最好保存一些。

4 个答案:

答案 0 :(得分:10)

是的确有所作为。

在Java中,2D数组是一维数组的数组,除了保存元素本身所需的空间外,数组(如所有对象)都有标题。

因此,请考虑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),并且引用是1个32位字,那么

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

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

但很明显,如果最大尺寸是最右边的尺寸,则使用较少的空间。


我已经使用int数组完成了数学计算,但在32位计算机上,intreference占用相同的字节数。在64位计算机上,引用的大小可以与intlong相同,具体取决于JVM选项。标题大小也可能不同....不完全确定......可能依赖于平台。

我没有说明保留Bitmap个对象本身所需的空间,但是你组织数组是一样的。

答案 1 :(得分: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);
    }
}

答案 2 :(得分: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的数组。

Reference

  

在诸如C的语言中,二维数组(或实际上任何多维数组)本质上是具有明智指针操作的一维数组。在Java中并非如此,其中多维数组实际上是一组嵌套数组。这意味着二维数组的每一行都有一个对象的开销,因为它实际上是一个单独的对象!

答案 3 :(得分:-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]);
    }
}