1

メモリを割り当てて永久に実行するサンプル Java アプリケーションを作成しました。
なぜサバイバー スペースが使用するメモリは 0k バイトなのですか?!

    List<String> stringlist = new ArrayList<String>();

    while (true) {
        stringlist.add("test");
        if (stringlist.size() >= 5000000)
            break;
    }

    while (true)
        for (String s : stringlist);
4

2 に答える 2

7

「test」は文字列リテラルであるため、ヒープではなく永続メモリに格納されます。作成するオブジェクトのメモリサイズは5000000+4 * 2〜5MBで、Edenスペースに簡単に収まります。

変更

stringlist.add("test");

stringlist.add(new String("test"));

5000000 * 4 * 2 = 38MBになりますが、これはおそらくEdenに収まります。リストのサイズまたは文字列の長さを増やして、サバイバーがいることを確認できます。

于 2012-07-20T17:07:49.513 に答える
3

"test"Stringリテラルであり、格納方法に関係なく (これは Java 開発中に変更されました)、ここで重要な点は、それが単一のオブジェクトであるということです。

Java 言語仕様を思い出してください

…文字列リテラルは常に class の同じインスタンスを参照しますString。これは、文字列リテラル (より一般的には、定数式の値である文字列 (§15.28)) が、メソッド String.intern を使用して一意のインスタンスを共有するために「インターン」されているためです。

したがって、常に同じインスタンスを参照するため、Stringループ内に新しい s が作成されることはありません。の内部容量が使い果たされた場合にのみ、ヒープの変更が発生します。"test"StringArrayList


の内部配列に最終的に必要なメモリArrayListは、オブジェクト参照のサイズによって異なります。通常は、5000000*4 bytes圧縮 oops を使用する 32 ビット JVM および 64 ビット JVM5000000*8 bytes用であり、圧縮 oops を使用しない 64 ビット JVM 用です。

ここでの興味深い点は、www.kdgregory.comで説明されています。

オブジェクトが十分に大きい場合は、tenured 世代で直接作成されます。ユーザー定義のオブジェクトは、この動作をトリガーするのに必要な数のメンバーを持っていません (すべきではありません!) が、配列は次のようになります。

これは、oracle.com にある次の言葉と一致します。

Survivor 領域が小さすぎる場合、コレクションのコピーは Tenured 世代に直接オーバーフローします。

これは、サバイバー スペースに大きな配列が表示されない別の理由です。したがって、それらが Eden 空間から Tenured Generation にコピーされたために表示されないか、そもそも Tenured Generation で作成されたために表示されないかは、正確な構成によって異なります。しかし、サバイバースペースに現れないという結果は同じです。


そのため、デフォルト容量の でArrayListが作成されると、内部配列はこのしきい値よりも小さくなり、容量を拡大するたびに次の配列も作成されます。ただし、新しい配列がこのしきい値を超えると、古い配列はすべてガベージになり、「生存者」として表示されなくなります。10

したがって、最初のループの終わりには、サイズがしきい値をはるかに超えているため、Survivor スペースをバイパスした配列が 1 つだけ残っています。2 番目のループは、メモリ管理に何も追加しません。一時的な を作成しますIteratorが、これらは決して「生き残る」ことはありません。

于 2014-08-11T13:23:25.123 に答える