2011-11-07 10 views
3

私のプログラムが増加しているメモリを見つけたのは、以下のコードのためです。現在、私は約7GBのファイルを読んでいます。そして、ハッシュセットに格納されるファイルは10Mですが、メモリ私のプログラムは300MBまで増加し続け、OutofMemoryErrorのためクラッシュします。ハッシュセットの問題であれば、どのデータ構造を選択すればよいですか?なぜ私のハッシュセットはメモリを消費するのですか?

あなたが本当に何をやっている私たちに語ったが、していない
if(tagsStr!=null) { 
     if(tagsStr.contains("a")||tagsStr.contains("b")||tagsStr.contains("c")) { 
      maTable.add(postId); 
     } 
    } else { 
     if(maTable.contains(parentId)) { 
      //do sth else, no memories added here 
     } 
    } 
+1

多くのデータを入れない限り、HashSetの問題は起こりそうにないと思います。格納している文字列のサイズは?ファイル全体を一度にメモリまたは1行に読み込んでいますか?ここで提供したデータは、実際に役立つ情報を十分に提供していません。 –

+0

テーブルがクラッシュするまでにいくつのアイテムが含まれていますか? –

+1

そして要素の平均の長さ/サイズはどれくらいですか? –

答えて

2

メモリリークが発生しているか、格納している文字列データの量が間違っています。私たちは、あなたのコードをもっと見ていないとわからない。

科学的な解決策は、メモリプロファイラを使用してアプリケーションを実行し、予期せず大量のメモリを使用しているデータ構造を確認するために出力を分析することです。


私は推測した場合、それは(いくつかのレベルでの)アプリケーションは、このような何かをやっていることを次のようになります。

String line; 
while ((line = br.readLine()) != null) { 
    // search for tag in line 
    String tagStr = line.substring(pos1, pos2); 
    // code as per your example 
} 

は、これは、あなたが期待するよりも多くのメモリを使用しています。 substring(...)コールは、元のline文字列のバッキング配列を参照するtagStrオブジェクトを作成します。短いと思われるタグ文字列実際にはは、元の行のすべての文字を保持するchar[]オブジェクトを参照しています。

修正はこれを行うことです。

String tagStr = new String(line.substring(pos1, pos2)); 

これは、引数文字列の補助配列を共有しないStringオブジェクトを作成します。

更新 - このようなものは、あなたの最新のデータがあれば、ますます起こりそうな説明です。


Jon Skeetの別のポイントで拡張すると、小さなStringのオーバーヘッドは驚くほど高くなります。例えば、典型的な32ビットのJVMに、1つの文字列のメモリ使用量がある:文字列オブジェクトの

  • Stringオブジェクトヘッダ:2つのワード
  • Stringオブジェクトフィールド:3つのワード
  • パディング:1ワード(と思う)
  • バッキング配列オブジェクトヘッダ:3つのワード
  • バッキング配列データ:1つのワード

合計:10ワード - 40バイト - 保持する一つのデータの...または入力が8ビット文字セットの場合はbyteのデータです。

(これはあなたの問題を説明するのに十分ではありませんが、とにかくそれを認識する必要があります。)

+0

私は一般的には、文字列のバッキング配列を共有することでメモリ消費量を減らすことができるということを付け加えたいと思います。これは、バッキング配列を共有している文字列の数とバッキング配列のどの部分がいずれの文字列でも使用されていないかによって異なります。 – jmg

+0

理論的には可能ですが、OPのケースではそう思わないでしょう。 –

6

  • あなたのファイルはASCIIのようなもので、現在であれば、あなたが読んで各文字は、ファイル内の1バイトになりますかメモリの2バイト。
  • 各文字列は、オブジェクトのオーバーヘッドを持っています - あなたはBufferedReader(または大規模な文字列から部分文字列を取る)とのラインを読んでいる場合は、それぞれが大きいことがあり、小さな文字列
  • の多くを保存している場合はこれが重要になりますこれを避けるためにmaTable.add(new String(postId))を使用するとよいでしょう。
  • ハッシュセットの各エントリは、キー/ハッシュコード/値/ネクストエントリ値を保持するために別個のオブジェクトを必要とします。繰り返しますが、これは要するに

を追加できるエントリの多くで、それはあなたが間違って何もしないしていることを十分に可能ですが、メモリ増加の要因の組み合わせは、あなたに対して働いています。これらのほとんどはやむを得ないものですが、第3のものはとすることができます。

+0

3番目の点は「BufferedReaderで行を読む?私はBufferedReaderが新しいString(...)を使用するように気を配ったと思った。私は部分文字列に関するビットに同意します。 –

+0

@PaulCager:それは確かに私が見た最後の時間はありませんでした。最後に、バッファのchar配列(デフォルトは80文字のIIRC)を読み込み、その文字配列のビューである新しい 'String'を作成しました。配列が "使用可能な文字列"よりもはるかに大きい場合は、多くのメモリを無駄にすることがあります。これはしばらく前のことだったので、変更されている可能性があります。 –

+0

これは変更されているように見える - これは、StringBuffer(overflows cb)を介して構築されたStringを返すか、 "str = new String(cb、startChar、i - startChar);"ここで、cbはバッファです。 –

0

それは(7Gファイルから)メモリに読み込まれたデータが何らかの形で解放されていない可能性があることができませんでした?私はジョンが置く何か... ie。文字列が不変であるため、文字列の読み込みには、新しい文字列オブジェクトの作成が必要です.GCが十分速くないとメモリ不足になる可能性があります。

上記の場合は、コードに「ブレークポイント」を挿入する可能性があります/繰り返し、すなわち。いくつかの定義されたポイントで、gcを発行し、終了するまで待つ。

+0

GCが十分に速くない場合は、OOMを取得しません。必要に応じて、GCはVM全体を一時停止し、OOMをスローする前に世界を止めるコレクションを行います。 –

+0

ありがとうございます。実際、私は、GCがブロッキングモードで自動的に起動されるという事実に気づいていませんでした:)しかし、この方法でもOOMがまだ発生しているようです。関連する質問を参照してください:http://stackoverflow.com/questions/1393486/what-does-the-error-message-java-lang-outofmemoryerror-gc-overhead-limit-excee具体的にはそれが参照する公式の記事:http: //www.oracle.com/technetwork/java/javase/gc-tuning-6-140523.html#par_gc.oom – Gyula

+0

"ガベージ・コレクションに時間がかかっていると、並列コレクタはOutOfMemoryErrorをスローします。総時間の98%がガベージコレクションに費やされ、ヒープの2%未満が回復されると、OutOfMemoryErrorがスローされます。つまり、GCがあまりにも多すぎると(あまりにも多くのオブジェクトが余りにもありません)、OOMがスローされる可能性があります。したがって、このような場合にアプリケーション駆動型GCが役立つかもしれませんが、そうではありませんか? – Gyula

0

-XX:+ HeapDumpOnOutOfMemoryErrorでプログラムを実行してください。 MATのようなメモリアナライザを使用して、すべてのメモリを使い切っているかどうかを確認することができます。

+0

ありがとう、私はMATを使用してみましたが、このように失敗しました:java_pid4080.hprofにヒープをダンプする... ダンプファイルが不完全です:十分なスペースこれを解決する方法はありますか? – faz

+1

ディスク容量が足りなくなったようです。 –

関連する問題