10

に何バイトが割り当てられaますbか?

import android.graphics.Bitmap;

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

純粋な配列が使用するメモリについてのみ質問していることに注意してください。内部にオブジェクトはありません。

なぜ私は尋ねているのですか?私はAndroidゲームを書いているからです。私にとっては順序は関係ありませんが、メモリに違いがある場合は、いくらか保存しておくとよいでしょう。

4

4 に答える 4

10

はい、違いはあります。

Javaでは、2D配列は1D配列の配列であり、配列(すべてのオブジェクトと同様)には、要素自体を保持するために必要なスペースに加えて、ヘッダーがあります。

したがって、int[10][2]int[2][10]を検討し、32ビットJVMを想定します。

  • int[2][10]2つの要素の1つの配列と、10の要素の2つの配列で構成されます。合計-3つの配列オブジェクト+22の要素。
  • int[10][2]10個の要素の1つの配列と、2個の要素の10個の配列で構成されます。合計-11個の配列オブジェクト+30個の要素。

ヘッダーサイズが332ビットワード(通常は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ビットマシンでは、JVMオプションに応じてint、参照はまたはと同じサイズにすることができます。longヘッダーのサイズも異なる場合があります....正確にはわかりません...プラットフォームに依存する可能性があります。

Bitmapオブジェクト自体を保持するために必要なスペースについては説明していませんが、配列を整理しても同じです。

于 2013-03-11T13:09:38.890 に答える
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 に答える
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をスローし、2番目のものはパスします。

その理由は、最初のバージョンでは長さ2の500.000配列が作成され、2番目のバージョンでは長さ500.000の2つの配列が作成されるためです。

参照

Cなどの言語では、2次元配列(または実際には任意の多次元配列)は、基本的に、適切なポインター操作を備えた1次元配列です。これは、多次元配列が実際にはネストされた配列のセットであるJavaには当てはまりません。これは、2次元配列のすべての行には、実際には個別のオブジェクトであるため、オブジェクトのオーバーヘッドがあることを意味します。

于 2013-03-11T13:13:48.247 に答える
-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 に答える