読者です 読者をやめる 読者になる 読者になる

Java の多次元配列のメモリ使用量について調べてみた

Java の多次元配列のメモリ使用量について調べてみたので、ちょっとメモしておきます。

C 言語だと、例えば int i[N][M] な多次元配列のメモリ使用量は、int が 4 byte なので 4 * N * M と単純に計算できる。

■ ArraySize.c

#include <stdio.h>

void main()
{
  int i[20000][10];
  int msize = sizeof(i);
  printf("%d\n", msize);
}

■ 実行結果

[root@centos62 c]# ./ArraySize
800000


ただ、Java の多次元配列は C 言語とかのそれとは違うらしく、上記みたいな 2 次元配列の場合、「1 次元配列のフィールドに 1 次元配列がある」みたいな感じになるようです。

・Arrays (The Java Tutorials > Learning the Java Language > Language Basics)
https://docs.oracle.com/javase/tutorial/java/nutsandbolts/arrays.html

In the Java programming language, a multidimensional array is an array whose components are themselves arrays. This is unlike arrays in C or Fortran.

以下のドキュメントで多次元配列がわかりやすく図示されてます。

・Arrays
http://www.oracle.com/technetwork/community/join/member-discounts/ocajavase7ch04-1840093.pdf

以下もわかりやすく説明されてます ( 要は 正確には Java に多次元配列はないと )。

・A Guide to Optimization of Java Programs (JavaWorld 2000/9)
http://www.shudo.net/article/200009-JavaWorld-optimization/#java_techniques

「1 次元配列のフィールドに 1 次元配列がある」ということなので、int i[N][M] の場合、メモリ使用量の計算は、([フィールドの配列のサイズ] * N + α) + ((4 * M + α) * N)になるはず。

で、[フィールドの配列のサイズ] や α については、以下の「Java 配列オブジェクトの分析」あたりに情報があります。

Java コードから Java ヒープまで
http://www.ibm.com/developerworks/jp/java/library/j-codetoheap/

[フィールドの配列のサイズ] が 4 byte ( *1 )、α が 128 bit ( 16 byte ) という感じです。

( *1 ) 配列のフィールドのサイズについては「表 1」に情報あるけど、ここに "配列" の記載がない。まぁ、配列はオブジェクトの一種なので「オブジェクト・フィールド」の 32 bit ( 4 byte ) ということでしょうか。

以上踏まえると、Java で int i[20000][10] な多次元配列のメモリ使用量は以下になるはず。

(4 * 20000 + 16) + ((4 * 10 + 16) * 20000) = 1200016 byte

実際に確認すると、まぁなかなか誤差ある感じですが・・・それなりの値になってる ( と思うことにします )。

■ ArraySize.java

class ArraySize {
  public static void main(String[] args) {
    System.out.println(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory());
    int i[][] = new int[20000][10];
    System.out.println(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory());
  }
}

■ 実行結果

[root@centos62 c]# java ArraySize
477312
1643280

※ 1643280 - 477312 = 1165968 byte ( 結構デカイ誤差やな・・・ )

ちょっと最後の検証結果が残念な感じですが、少なくとも C 言語みたいに単純に 4 * N * M みたいにはならないことはわかったというところで、以上とします。

[ 検証環境 ]
CentOS 6.2 ( 32 bit )
Java SE 8
GCC 4.4.7