1

私は、データベースから100万行を読み取り、それらをファイルに書き込む単純なJavaプログラムを作成しました。

このプログラムが使用できる最大メモリは512Mです。

このプログラムが500K行を超えるメモリ不足で実行されていることによく気づきます。

このプログラムは非常に単純なプログラムであるため、メモリリークがないことが簡単にわかります。プログラムが機能する方法は、データベースから1000行をフェッチし、Streamsを使用してそれらをファイルに書き込み、次の1000行をフェッチすることです。各行のサイズは異なりますが、どの行も巨大ではありません。プログラムの実行中にダンプを取ると、古い文字列がヒープ上に簡単に表示されます。ヒープ内のこれらの文字列には到達できません。つまり、ガベージが収集されるのを待っています。また、このプログラムの実行中にGCが必ずしも実行されるとは限らないため、Stringがヒープ内に必要以上に長く残ると思います。

解決策は、DBによって返される行を格納するためにStringオブジェクトを使用する代わりに、長いChar配列(またはStringbuffer)を使用することだと思います。Char配列の内容を上書きできると想定しています。つまり、毎回新しいスペースを割り当てることなく、同じChar配列を複数の反復で使用できます。

擬似コード:

  1. new char[1000][1000]を使用して配列の配列を作成します。
  2. DBからアレイへの1000行を埋めます。
  3. 配列をファイルに書き込みます。
  4. 次の1000行に同じ配列を使用する

上記の擬似コードで問題が解決した場合、文字列が使用されなくなったとしても、文字列が使用したスペースを直接要求する方法がないため、実際には文字列クラスの不変の性質がJavaプログラマーを傷つけます。

この問題に対するより良い代替案はありますか?

PS:私は静的分析だけをしませんでした。yourkitプロファイラーを使用してヒープダ​​ンプをテストしました。ダンプには、文字列の96%にGCルートがないことが明確に示されています。これは、ガベージコレクションを待機していることを意味します。また、コードではサブストリングを使用していません。

4

3 に答える 3

2

クラスの不変性は、とStringはまったく関係ありませんOutOfMemoryError。不変性とは、変更できないことを意味します。それだけです。

メモリが不足している場合は、ガベージコレクタが収集するガベージを見つけることができなかったことが原因です。

実際には、メモリ内に非常に多くの文字列への参照を保持している可能性があります(たとえば、List、Set、Mapなどの文字列を保持するコレクションはありますか?)。ガベージコレクタがその役割を果たし、メモリを解放できるようにするには、これらの参照を破棄する必要があります。

于 2012-10-16T08:28:03.133 に答える
1

この質問に対する単純な答えは「いいえ」です。あなたが思っているよりも長く参照にぶら下がっているのではないかと思います。

それらのストリームを適切に閉じていますか? あなたはintern()それらの弦を鳴らしていますか?その結果、文字列がまだ存在しない場合は永続的なコピーが作成され、permgenスペース (収集されない) が占有されます。より大きな文字列を取っていsubstring()ますか?文字列は flyweight パターンを利用し、 を使用して作成された場合は文字配列を共有しsubstring()ます。詳しくはこちらをご覧ください。

ガベージ コレクションが実行されていないことをお勧めします。このオプション-verbose:gcはガベージ コレクションをログに記録し、何が起こっているかをすぐに確認できます。

于 2012-10-16T08:29:06.227 に答える
1

OutOfMemoryError を引き起こす可能性のある文字列に関する唯一のことは、はるかに大きな文字列の小さなセクションを保持する場合です。これを行っている場合は、ヒープ ダンプから明らかなはずです。

ヒープ ダンプを取得するときは、ライブ オブジェクトのみを確認することをお勧めします。この場合、保持されている必要のないオブジェクトは、コードのバグである可能性が最も高くなります。

于 2012-10-16T08:31:05.427 に答える