【问题标题】:Does order in a declaration of multidimensional array have an influence on used memory?多维数组声明中的顺序对已用内存有影响吗?
【发布时间】:2013-02-26 16:06:57
【问题描述】:

将为ab 分配多少字节?

import android.graphics.Bitmap;

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

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

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

【问题讨论】:

    标签: java arrays memory


    【解决方案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]);
        }
    }
    

    【讨论】:

    • 您通常在嵌套循环中处理多维数组内容。因此,您的数组应该以您处理内部循环中的相邻元素的方式组织,以允许编译器生成最有效的代码。我不知道 java 如何组织内存,但认为它与 C/C++ 没有什么不同: int a[10[100]; for (i = 0; i
    【解决方案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);
        }
    }
    

    【讨论】:

    • 这不是优化的吗?差别很大,很多人都没有意识到这一点。在 [] 之间按升序写的这种排序数字应该是可行的,不是吗?
    • @AdamStelmaszczyk 我知道这是不同的,但从未计算过差异。它确实是巨大的。而且我看不出它为什么不能优化,因为你不能“移动指针”所以我看不出它会产生什么负面影响......
    【解决方案3】:

    是的,它确实有所作为。

    在 Java 中,二维数组是一维数组的数组,数组(与所有对象一样)除了包含元素本身所需的空间外,还具有标题。

    因此考虑 int[10][2]int[2][10],并假设 JVM 是 32 位。

    • 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 位机器上intreference 占用相同的字节数。在 64 位机器上,引用的大小可以与 intlong 相同,具体取决于 JVM 选项。标头大小也可能不同....不确定...可能取决于平台。

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

    【讨论】:

    • 谢谢!我有一个问题。也许节省内存的最佳方法是Bitmap[] c = new Bitmap[2*1000],然后计算像1000*i + j这样的索引?与Bitmap[2][1000]相比不会有速度差异?
    • 1) 是的……尽管它使您的代码更难阅读并且增量节省不是很好。 2) 一维数组可能会更快,因为数组边界检查和获取更少。但是,差异很可能太小而无法产生任何差异。
    • 请注意,例如在 64 位热点上,引用默认压缩为 4 个字节。
    • @assylias - 没错,但这不会影响整体情况。
    • @StephenC 它没有 - 只是一个旁注。
    【解决方案4】:

    是的,它有所作为。用-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 中情况并非如此,其中多维数组实际上是一组嵌套数组。这意味着二维数组的每一行都有一个对象的开销,因为它实际上是一个单独的对象!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-01
      • 2019-12-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多