-1

私はJavaの文字が2バイトであることを知っています。ただし、リストにいくつかの文字をロードすると、文字を保存するのに87Bのコストがかかります。テストは次のようになります。

995328行を含むファイル「ソース」があります。すべての行は単なる文字です:「a」。(したがって、すべての文字を保存するには、Javaでほぼ2MBのコストがかかります)。

ソースコードで呼び出されるsleepメソッドは2つあり、topコマンドを使用してメモリ使用量をいつでも確認できます。

最初のsleep(10000)メソッドを実行するときのRSIZE値は25Mであり、2番目のsleepメソッドを実行するときのRSIZE値は108Mです。したがって、文字列ごと(これは単なる「a」です)のコスト:(108MB-25MB)/ 995328=87B。文字列「a」がなぜこれほど多くのメモリを消費するのかわかりません!!! 誰かが私の理由を教えてもらえますか?

public static void main(String[] args) throws Exception{
    File file = new File("source");
    BufferedReader br = new BufferedReader(new FileReader(file));
    String line = null;
    List<String> list = new ArrayList<String>();
    Thread.sleep(10000); 
    while((line = br.readLine())!=null){
        list.add(line);
    }
    Thread.sleep(10000);

}
4

5 に答える 5

3

@Amirは、メモリ使用量を測定するためにtopよりも優れた方法があると言うのは正しいです(たとえば、hprofはJDKに含まれています)が、メモリ数を混乱させるいくつかのより深い問題があります。

  1. fileまたはを閉じていませんbr。これは大きなものです。これらのオブジェクトはそれぞれ、オペレーティングシステムのファイルI/Oライブラリとインターフェイスするための一連のネイティブコードのラッパーです。これらのリソースにはファイルハンドルとキャッシュバッファが含まれるため、ファイルから読み取っているデータの一部は、メモリ使用量で2回カウントされます。1回はに接続されたキャッシュで、もう1回はでカウントされbrますlist
  2. 各文字列は、実際には単なる文字のシーケンスではありません。JREは、他のデータとともに、文字配列、開始インデックス、および長さへのポインターを維持します。文字配列へのポインタは8バイト、開始インデックスは4バイト、長さは4バイトです。一部のフィールドを省略していることは確かですが、この控えめな見積もりでさえ、文字列の実際の文字を無視して、文字列に16バイトのオーバーヘッドが発生します。
  3. list変数にもオーバーヘッドがあります。各スロットがポインタ(さらに8バイト)であるバッキング配列があり、空のスロットがたくさんあります。ArrayList配列のサイズ変更(つまり、新しい配列を作成し、古い配列からすべての要素をコピーする)は高価であり、空のスロットはそれぞれ8であるため、バッキング配列が行に対応するように大きくなると、クラスに余分なスペースが残ります。 64ビットシステムではバイト。
  4. topから返される数値には、ガベージが含まれます。ガベージコレクターはJVMの実装とバージョンによって異なりますが、通常は新しいオブジェクトをすばやく収集し、メモリ不足がある場合にのみ古いオブジェクトを収集します。したがって、のバッキングストアのサイズ変更で残った余分な配列はすべて、ArrayListメモリ内に残っている可能性が高く、上位の数値にカウントされます。これらの配列は最初は大きいため(ほとんどの場合、少なくとも500Kスロットの配列があり、それぞれが8バイトのポインターです)、これによりプログラムの合計メモリ使用量が増加します。

注意:64ビットシステムを想定して、上記の8バイトポインタについて説明しました。32ビットシステムでは、ポインタが4バイトしかないことを除いて、私が言ったことはすべて成り立ちます。

于 2012-04-13T05:56:34.137 に答える
2

私はこれらの数値を計算するためにトップに頼ることはありません。VisualVMのようなものを使用しないのはなぜですか?これにより、データ構造によって占有されているメモリの量が正確にわかります。

RSIZEこれには、JVM自体が使用するメモリを含む常駐メモリの合計が反映されていると思います。この問題は別として、ベンチマークでは、JVMによってまだ収集されていない到達不能オブジェクトは考慮されていません。プロファイラーのヒープスナップショットを使用すると、GCがトリガーされます。GCはこれを考慮に入れます。

于 2012-04-13T05:48:04.173 に答える
1

配列リストに文字だけを保存するのではなくString、ファイルの行ごとに1つのインスタンスを保存します。

私はこれらの計算を自分で行っていませんが、文字列のメモリ使用率に関するNeil Coffeysのチュートリアルによると、各文字列は次のようになります。

最小文字列メモリ使用量(バイト)= 8 *(int)((((文字なし)* 2)+ 45)/ 8)

ファイルに行ごとに1つの文字が含まれている場合、各文字列のコストは少なくとも8*((2+45) / 8)47バイトになります。

それに、arraylistのコストを追加します。

于 2012-04-13T05:56:43.417 に答える
0

作成している文字列とリストのコスト、特にその成長ポリシーを見落としています。Javadocを確認してください。私が内部で見たArrayListの実装は、リストがオーバーフローしたときにリストを50%増やします。

于 2012-04-13T05:54:00.097 に答える
0

Javaはガベージコレクションされた言語であるため、データ構造のサイズを推定するために、一部のコードの実行中に外部で測定された仮想メモリフットプリントの変化を確認することはできません。ガベージの蓄積だけでなく、ガベージの蓄積が原因である可能性があるヒープの増分を考慮に入れています。また、ガベージコレクションは、オブジェクトのライブセットを表すために必要なスペースよりもはるかに多くのスペースを提供する場合に適しているため、コレクションはそれほど頻繁ではありません。一般的に、スラックスペースが非常に少ない場合、ガベージコレクションは遅くなります。仮想マシンが、すべてのオブジェクトを表すために必要な最小限のメモリフットプリントに近い状態を維持している場合、パフォーマンスは非常に悪くなります。

于 2012-04-13T06:06:04.333 に答える