プリミティブのJava配列はスタックまたはヒープに格納されていますか?


85

私はこのような配列宣言を持っています:

int a[];

これaがプリミティブint型の配列です。この配列はどこに保存されますか?ヒープまたはスタックに格納されていますか?これはintプリミティブ型であり、すべてのプリミティブ型がヒープに格納されるわけではありません。


38
それは配列ではありません。配列への参照です。参照自体は、クラスまたはオブジェクトのメンバーである場合はヒープに、メソッドのローカル変数である場合はスタックに格納できます。また、プリミティブ型は、クラスまたはオブジェクトのメンバーである場合、ヒープに格納できます。
uncleO 2010年

回答:


143

gurukulkiが言ったように、それはヒープに保存されます。しかし、あなたの投稿は、おそらく「プリミティブは常にスタックに住んでいる」という神話を広めている善意のある人による誤解を示唆していました。これは真実ではありません。ローカル変数の値はスタックにありますが、すべてのプリミティブ変数がローカルであるとは限りません。

たとえば、次のことを考慮してください。

public class Foo
{
    int value;
}
...

public void someOtherMethod()
{
    Foo f = new Foo();
    ...
}

さて、どこにf.value住んでいますか?神話はそれがスタック上にあることを示唆しますが、実際にはそれは新しいFooオブジェクトの一部であり、ヒープ1上に存在します。(fそれ自体の値は参照であり、スタック上に存在することに注意してください。)

そこから、配列への簡単なステップです。配列は単なる多くの変数であると考えることができます-したがってnew int[3]、この形式のクラスを持つことに少し似ています:

public class ArrayInt3
{
    public readonly int length = 3;
    public int value0;
    public int value1;
    public int value2;
}

1実際、これよりも複雑です。スタック/ヒープの区別は、ほとんどが実装の詳細です。一部のJVMは、おそらく実験的なものであり、オブジェクトがメソッドから「エスケープ」されないことを認識でき、オブジェクト全体をスタックに割り当てる可能性があると思います。ただし、気にすることを選択した場合、概念的にはヒープにあります。


1
Javaの「escapeanalyisis」について:blog.juma.me.uk/2008/12/17/objects-with-no-allocation-overhead JDK 6 Update14の早期アクセスリリース以降に存在しているとのことです。 JDK 6アップデート23以降はデフォルトで有効になって
グイド

配列がpublicstatic finalの場合、何か変更されますか?それなら、それは定数プールの一部であるべきではありませんか?
Malachiasz 2014

@Malachiasz:いいえ。配列が定数になることはありません。
Jon Skeet 2014

@JonSkeet:私が知っているJavaライブラリのすべてのバージョンで、すべてStringchar[]。によって支えられています。リテラル文字列はパブリック定数プールに格納されていると思います。GC最適化の場合、これはバッキングアレイも同様に格納する必要があることを意味します(そうでない場合、バッキングアレイが収集の対象となるGCサイクル中に定数プールをスキャンする必要があります)。
スーパーキャット2014年

@supercat:はい、それは理にかなっています。ただし、自分で宣言した配列が定数プールの一部になることはありません。
ジョンスキート2014年

37

ヒープに格納されます

配列はJavaのオブジェクトだからです。

編集 :あなたが持っている場合

int [] testScores; 
testScores = new int[4];

このコードは、コンパイラに「4つのintを保持する配列オブジェクトを作成し、それをという名前の参照変数に割り当てますtestScores。また、各int要素をゼロに設定します。ありがとう」と考えてください。


2
そして、testScores(ヒープ上の配列を指す)という名前の参照変数がスタックにあります。
ザキ2010

11
-gコンパイラにオプションを指定した場合にのみ、コードに「ありがとう」と表示されます。それ以外の場合は、最適化されます。
暴徒

1
@mobこれが唯一のコードであると想定しています。これらの2行は、実際に配列を使用するより大きなプログラムの一部であると想定する方がおそらく良いでしょう。
コード-見習い

22

これは、それ自体がプリミティブではないプリミティブ型の配列です。経験則として、新しいキーワードが含まれると、結果はヒープに表示されます。


18

このテーマで実行したいくつかのテストを共有したかっただけです。

サイズ1000万の配列

public static void main(String[] args) {
    memInfo();
    double a[] = new double[10000000];
    memInfo();
}

出力:

------------------------
max mem = 130.0 MB
total mem = 85.0 MB
free mem = 83.6 MB
used mem = 1.4 MB
------------------------
------------------------
max mem = 130.0 MB
total mem = 130.0 MB
free mem = 48.9 MB
used mem = 81.1 MB
------------------------

ご覧のとおり、使用済みヒープサイズは約80 MB増加します。これは、10m * sizeof(double)です。

しかし、doubleの代わりにDoubleを使用している場合

public static void main(String[] args) {
    memInfo();
    Double a[] = new Double[10000000];
    memInfo();
}

出力には40MBが表示されます。二重参照のみがあり、初期化されていません。

ダブルで埋める

public static void main(String[] args) {
    memInfo();
    Double a[] = new Double[10000000];      
    Double qq = 3.1d;
    for (int i = 0; i < a.length; i++) {
        a[i] = qq;
    }
    memInfo();
}

それでも40MB。それらはすべて同じDoubleオブジェクトを指しているからです。

代わりにdoubleで初期化

public static void main(String[] args) {
    memInfo();
    Double a[] = new Double[10000000];
    Double qq = 3.1d;
    for (int i = 0; i < a.length; i++) {
        a[i] = qq.doubleValue();
    }
    memInfo();
}

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

ライン

a[i] = qq.doubleValue();

と同等です

a[i] = Double.valueOf(qq.doubleValue());

これは

a[i] = new Double(qq.doubleValue());

毎回新しいDoubleオブジェクトを作成するので、ヒープを吹き飛ばします。これは、Doubleクラス内の値がヒープに格納されていることを示しています。


1
memInfo()plsのコード詳細を貼り付けてもらえますか?:)
hedleyyan

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.